執行緒基礎

執行緒生命週期

  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

特性RunnableCallable
方法run()call()
返回值void泛型 V
異常處理不能拋出 checked exception可拋出 Exception
JDK 版本1.01.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 0
UserThreadDemo.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()
所屬類別ObjectThread
釋放鎖
使用條件必須在 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();
}

參考資源