在 Java 並發程式設計中,建立執行緒是最基礎也最關鍵的技能。Java 提供了三種標準方式來實現多執行緒,各有適用場景與優缺點。掌握這三種方式的核心差異,能幫助開發者根據業務需求選擇最合適的方案。
這是最直觀的方式,直接繼承 java.lang.Thread 類別,並覆寫其 run() 方法來定義執行緒的執行邏輯。
public class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000); // 讓執行緒休眠 1 秒,模擬耗時操作
System.out.println("Thread 執行緒執行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 啟動執行緒
MyThread thread = new MyThread();
thread.start(); // 呼叫 start() 而非 run(),才能真正開啟新執行緒
特點: 程式碼簡單直觀,但因 Java 單繼承限制,若類別已繼承其他父類別則無法使用此方式。sleep() 會讓執行緒進入阻塞狀態並釋放 CPU 資源,但不會釋放鎖資源。
實作 java.lang.Runnable 介面,將執行邏輯放在 run() 方法中,再將實例傳給 Thread 建構子。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable 執行緒正在執行...");
}
}
// 建立並啟動執行緒
Thread thread = new Thread(new MyRunnable());
thread.start();
優勢: 避免了單繼承限制,同一個 Runnable 實例可被多個 Thread 共享,適合多個執行緒處理同一資源的場景(如賣票系統)。也是執行緒池技術的基礎。
Callable 是 Runnable 的增強版,定義於 java.util.concurrent 套件中,主要差異在於:
call() 方法可回傳泛型結果,而 run() 無回傳值call() 可拋出受檢異常,run() 不能FutureTask 可取得執行結果、取消任務、判斷是否完成public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "Callable 任務執行結果";
}
}
// 使用 FutureTask 包裝並啟動
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
// 取得結果(會阻塞直到任務完成)
String result = futureTask.get();
System.out.println("任務回傳:" + result);
| 特性 | Thread 繼承 | Runnable 實作 | Callable 實作 |
|---|---|---|---|
| 定義方法 | run() | run() | call() |
| 回傳值 | 無 | 無 | 有(泛型) |
| 拋出異常 | 不可拋受檢異常 | 不可拋受檢異常 | 可拋受檢異常 |
| 適用場景 | 簡單獨立任務 | 共享資源、執行緒池 | 需結果、異常處理、異步運算 |
| 執行方式 | 直接 start() | Thread 建構子包裝 | FutureTask + Thread/執行緒池 |
ExecutorService)ExecutorService 提交 Runnable 或 Callable,避免手動管理 Thread 生命週期理解這三種方式的本質差異,能讓你在面對不同業務複雜度時,寫出更健壯、可維護的並發程式碼。