執行緒基礎
執行緒生命週期
flowchart TB
n((New)) ==> |start()|R{{Runnable}}
R <--> |排班器 / yield|Ring{{Running}}
Ring ==> |run() 完畢|d((Dead))
Ring ==> |阻斷事件: IO, join, sleep...| Bl{{Blocked}}
Bl ==> |阻斷結束| R
Ring ==> |擁有鎖的 Thread 呼叫 wait()|WP{{Wait Pool}}
WP ==> |notify / notifyAll / interrupt| LP{{Lock Pool}}
LP ==>|取得鎖| R
建立執行緒的方式
繼承 Thread 類別
ExtendsThread.java
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread extends Thread - run()");
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}實作 Runnable 介面
ImplementsRunnable.java
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread implements Runnable - run()");
}
}
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable(), "t1");
t.start();
}實作 Callable 介面
- 有返回值
- 可拋出異常
ImplementsCallable.java
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Thread implements Callable - call()");
return "Hello Callable";
}
}
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread t = new Thread(futureTask, "t1");
t.start();
System.out.println("返回值: " + futureTask.get());
}使用執行緒池
UseThreadPool.java
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
System.out.println("Task running in thread pool");
});
executor.shutdown();Runnable vs Callable
| 特性 | Runnable | Callable |
|---|---|---|
| 方法 | run() | call() |
| 返回值 | void | 泛型 V |
| 異常處理 | 不能拋出 checked exception | 可拋出 Exception |
| JDK 版本 | 1.0 | 1.5 |
classDiagram
class FutureTask{
Callable
}
class Callable
<<interface>> Callable
class Runnable
<<interface>> Runnable
Callable --o FutureTask
Runnable <|.. FutureTask
守護執行緒 (Daemon Thread)
守護執行緒是為其他執行緒提供服務的特殊執行緒,在背景默默執行系統性服務(如 GC)。
當系統只剩下守護執行緒時,JVM 會自動退出。
DaemonDemo.java
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " working, " +
(Thread.currentThread().isDaemon() ? "Daemon Thread" : "User Thread"));
while (true) {}
}, "t1");
t1.setDaemon(true); // 設定為守護執行緒
t1.start();
Thread.sleep(Duration.ofSeconds(3));
System.out.println(Thread.currentThread().getName() + " --- main thread ends");
}
// Output:
// t1 working, Daemon Thread
// main --- main thread ends
// Process finished with exit code 0UserThreadDemo.java
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " working, " +
(Thread.currentThread().isDaemon() ? "Daemon Thread" : "User Thread"));
while (true) {}
}, "t1");
t1.start(); // 預設為 User Thread
Thread.sleep(Duration.ofSeconds(3));
System.out.println(Thread.currentThread().getName() + " --- main thread ends");
}
// Output:
// t1 working, User Thread
// main --- main thread ends
// (程式不會結束,因為 t1 仍在執行)ThreadLocal
ThreadLocal 為每個執行緒提供獨立的變數副本,避免共享狀態問題。
| 業務場景 | 相關框架 |
|---|---|
| Web 應用的使用者會話管理 | Spring Session, Servlet HttpSession |
| 日誌追蹤 | Logback, Slf4j, Log4j (MDC) |
| 資料庫連接/事務管理 | Spring Data, Hibernate, MyBatis |
| 執行緒池中的上下文傳遞 | Spring Framework |
使用執行緒池時,務必在任務結束時清理 ThreadLocal,避免記憶體洩漏。
執行緒間通訊
wait / sleep 差異
| 特性 | wait() | sleep() |
|---|---|---|
| 所屬類別 | Object | Thread |
| 釋放鎖 | 是 | 否 |
| 使用條件 | 必須在 synchronized 區塊內 | 任何地方 |
| 喚醒方式 | notify() / notifyAll() | 時間到自動醒來 |
虛假喚醒 (Spurious Wakeup):
wait() 應搭配 while 迴圈使用,而非 if,以避免虛假喚醒問題。wait / notify 範例
WaitNotifyDemo.java
synchronized (lock) {
while (!condition) { // 使用 while 避免虛假喚醒
lock.wait();
}
// 處理邏輯
}
// 另一個執行緒
synchronized (lock) {
condition = true;
lock.notifyAll();
}