核心方式對比
| 維度 | 繼承 Thread | 實現 Runnable |
| 本質 | Thread 本身實現了 Runnable | 純粹任務定義,與執行緒分離 |
| 繼承限制 | 單繼承,無法繼承其他業務類 | 無限制,可繼承任意類 |
| 資源共享 | 較難(需額外傳遞共享對象) | 天然支援:多執行緒共享同一 Runnable 實例 |
| 適用場景 | 簡單獨立任務、教學演示 | 生產環境標準寫法、線程池提交 |
方式一:繼承 Thread 類
public class DownloadThread extends Thread {
private final String fileName;
public DownloadThread(String fileName) {
this.fileName = fileName;
setName("Downloader-" + fileName); // 設定執行緒名稱,利於除錯
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 開始下載: " + fileName);
try {
Thread.sleep(2000); // 模擬下載耗時
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保留中斷狀態
return;
}
System.out.println(Thread.currentThread().getName() + " 下載完成: " + fileName);
}
public static void main(String[] args) {
new DownloadThread("report.pdf").start();
new DownloadThread("image.png").start();
}
}
方式二:實現 Runnable 接口(推薦)
public class DownloadTask implements Runnable {
private final String fileName;
public DownloadTask(String fileName) { this.fileName = fileName; }
@Override
public void run() {
String tName = Thread.currentThread().getName();
System.out.println(tName + " 開始下載: " + fileName);
try { Thread.sleep(2000); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); return; }
System.out.println(tName + " 下載完成: " + fileName);
}
public static void main(String[] args) {
// 1. 直接 new Thread 包裝
new Thread(new DownloadTask("doc.txt"), "Worker-1").start();
new Thread(new DownloadTask("data.csv"), "Worker-2").start();
// 2. 線程池提交(生產環境標準)
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new DownloadTask("big.zip"));
pool.submit(new DownloadTask("small.txt"));
pool.shutdown();
}
}
進階:Callable + Future 獲取返回值
public class CalcTask implements Callable<Integer> {
private final int n;
public CalcTask(int n) { this.n = n; }
@Override public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) sum += i;
Thread.sleep(100); // 模擬計算
return sum;
}
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Integer> f1 = es.submit(new CalcTask(100));
Future<Integer> f2 = es.submit(new CalcTask(200));
System.out.println("1+...+100 = " + f1.get()); // 阻塞等待結果
System.out.println("1+...+200 = " + f2.get());
es.shutdown();
}
}
關鍵最佳實踐
- 優先使用線程池:避免頻繁創建/銷毀執行緒,控制併發數,支援任務隊列與拒絕策略。
- 正確處理中斷:捕獲
InterruptedException 後調用 Thread.currentThread().interrupt() 保留中斷標誌。
- 共享資料同步:使用
synchronized、Lock、Atomic*、Concurrent* 等工具,避免競態條件。
- 執行緒名稱語義化:
ThreadFactoryBuilder.setNameFormat("order-process-%d"),便於日誌追蹤與監控。
- 避免在 run() 中吞沒異常:統一使用
Thread.setDefaultUncaughtExceptionHandler 或 Future.get() 捕獲。
- 優先用 CompletableFuture (Java 8+):鏈式組合、非阻塞回調、異常處理更優雅。
CompletableFuture 快速示例
CompletableFuture.supplyAsync(() -> fetchUser(1001), executor)
.thenApply(User::getOrders)
.thenCompose(orders -> CompletableFuture.supplyAsync(() -> enrich(orders), executor))
.whenComplete((result, ex) -> {
if (ex != null) log.error("失敗", ex);
else log.info("結果: {}", result);
});