多線程是 Java 並發編程的基石。本文系統講解:
① 線程與進程區別、五大狀態轉換
② 三種創建方式 (Thread/Runnable/Callable) 代碼對比
③ 為什麼推薦 Runnable、Callable 適用場景
④ 實戰中常見的睡眠/讓步/等待通知/同步阻塞細節
┌─────────┐ start() ┌─────────┐ CPU調度 ┌─────────┐
│ NEW │ ────────────────► │ RUNNABLE │ ──────────► │ RUNNING │
└─────────┘ └─────────┘ └────┬────┘
│
┌───────────────────────────────────────┘
▼
┌─────────────┐
│ BLOCKED │ ◄── sleep/wait/join/同步鎖/IO
└──────┬──────┘
│ 喚醒/超時/獲取鎖
▼
┌─────────────┐
│ RUNNABLE │ (回到就緒隊列)
└─────────────┘
│
│ run() 結束 / 異常
▼
┌─────────────┐
│ TERMINATED │
└─────────────┘
| 方法 | 釋放鎖 | 喚醒方式 | 進入狀態 |
|---|---|---|---|
| sleep(ms) | 否 | 時間到 | TIMED_WAITING |
| wait() | 是 | notify/notifyAll/超時 | WAITING/TIMED_WAITING |
| join() | 否 | 目標線程結束 | WAITING |
| LockSupport.park() | 否 | unpark() | WAITING |
| 同步鎖 | 否 | 獲取鎖 | BLOCKED |
class MyThread extends Thread {
@Override public void run() {
for (int i=0; i<10; i++)
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
new MyThread().start();
new MyThread().start();
輸出:兩線程交替打印,隨機搶佔 CPU
class MyRunnable implements Runnable {
@Override public void run() { /* 同邏輯 */ }
}
MyRunnable r = new MyRunnable();
new Thread(r).start(); // 線程1
new Thread(r).start(); // 線程2 共享同一 Runnable 實例 → 數據共享
優勢:邏輯與線程控制解耦、避免單繼承限制、天然支援數據共享
class MyCallable implements Callable<Integer> {
@Override public Integer call() { return 1; }
}
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
Integer result = task.get(); // 阻塞等待結果
System.out.println(result); // 1
適用:需獲取執行結果、拋出受檢異常的異步任務
| 維度 | Thread 繼承 | Runnable 實現 |
|---|---|---|
| 繼承限制 | 單繼承,若已繼承其他類則不可用 | 可多實現,無限制 |
| 數據共享 | 需額外傳遞共享對象 | 同一實例傳給多線程,天然共享 |
| 代碼復用 | 邏輯綁定線程類,難復用 | 邏輯獨立,可傳給線程池/CompletableFuture |
| 推薦度 | ⭐ (僅簡單練習) | ⭐⭐⭐⭐⭐ (生產首選) |
run() 只是普通方法調用,**不會開啟新線程**,必須調用 start()new Thread(r, "order-consumer-1"),日誌排查關鍵volatile boolean running 標誌位 + interrupt() 協作式停止setDaemon(true) 需在 start() 前設置,JVM 退出時不等待守護線程現代最佳實踐:業務代碼**不直接 new Thread**,統一提交至業務隔離線程池 (自定義 ThreadPoolExecutor),配合
CompletableFuture實現異步編排,框架層統一管理生命週期、監控、熔斷。