多線程是 Java 並發編程的基石。本文系統梳理線程創建、狀態流轉、常用方法、同步鎖、Lock 機制、死鎖條件與管道流通信,附完整代碼範例,助你建立扎實的並發基礎。
public class MyRunnable implements Runnable {
private final String name;
public MyRunnable(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " 第 " + i + " 次運行");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println(name + " 運行完畢停機");
}
}
// 測試啟動
public class Test {
public static void main(String[] args) {
new Thread(new MyRunnable("0號機")).start();
new Thread(new MyRunnable("1號機")).start();
}
}
優勢:避免單繼承限制,便於共享同一任務實例,適合資源共享場景。
public class MyThread extends Thread {
private final String name;
public MyThread(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " 第 " + i + " 次運行");
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println(name + " 運行完畢停機");
}
}
// 測試啟動
public class Test {
public static void main(String[] args) {
new MyThread("0號機").start();
new MyThread("1號機").start();
}
}
缺點:佔用繼承名額,每個線程需獨立實例,不利於任務與線程分離。
| 狀態 | 觸發條件 | 說明 |
|---|---|---|
| 初始 (NEW) | new Thread() | 線程對象創建,未調用 start() |
| 就緒 (RUNNABLE) | start() | 進入線程隊列,等待 CPU 調度 |
| 運行 (RUNNING) | 獲得 CPU | run() 正在執行 |
| 阻塞 (BLOCKED) | sleep()/wait()/I/O/同步鎖 | 暫時放棄 CPU,釋放或持有鎖視具體方法而定 |
| 死亡 (TERMINATED) | run() 結束 / stop() / 異常 | 線程生命週期結束 |
關鍵區別:sleep()不釋放鎖,佔用 CPU(計時);wait()釋放鎖,不佔用 CPU,需配合notify()/notifyAll()喚醒。
| 方法 | 作用 | 備註 |
|---|---|---|
start() | 啟動線程,由 JVM 調用 run() | 不可直接調用 run(),否則變單線程 |
sleep(ms) | 當前線程休眠指定毫秒 | 靜態方法,不釋放鎖 |
yield() | 當前線程讓出 CPU,重新競爭 | 不保證調度結果 |
join() | 當前線程等待目標線程結束 | 常用於主線程等待子線程 |
interrupt() | 中斷線程(設置中斷標誌) | 需配合 isInterrupted() 或捕獲 InterruptedException |
setPriority(int) | 設置優先級 1-10 | 建議用 MAX/MIN/NORM_PRIORITY 常量,跨平台可移植 |
setDaemon(true) | 標記為守護線程 | JVM 僅剩守護線程時自動退出 |
wait()/notify()/notifyAll() | Object 監視器方法,需在 synchronized 內調用 | 實現線程協作核心 |
this,同一實例的所有 synchronized 方法互斥。ClassName.class(類鎖),全類互斥。synchronized(obj) { ... },可精確控制鎖範圍與鎖對象。每個實例擁有唯一對象鎖,類擁有唯一類鎖,兩者互不干擾。同一線程可同時持有對象鎖與類鎖(可重入)。
Lock lock = new ReentrantLock();
try {
lock.lock(); // 獲取鎖,阻塞直到成功
// 臨界區代碼
return result; // return 必須在 try 內,確保 finally 執行
} finally {
lock.unlock(); // 釋放鎖,防止死鎖
}
| 特性 | synchronized | Lock |
|---|---|---|
| 鎖獲取失敗 | 無限等待 | tryLock() 可立即返回/超時返回 |
| 公平性 | 非公平 | 可構建公平鎖 new ReentrantLock(true) |
| 條件變量 | wait/notify | newCondition() 支援多條件隊列 |
| 性能 | 低競爭優,高競爭劣 | 高競爭下穩定 |
Java 中只能人為避免條件 3、4,編碼時遵循「鎖排序法」與「嘗試性獲取鎖(tryLock)」。
管道流提供單向通道,成對使用:
PipedInputStream / PipedOutputStreamPipedReader / PipedWriter// 創建與連接
PipedWriter writer = new PipedWriter();
PipedReader reader = new PipedReader(writer); // 或 writer.connect(reader)
// 生產者線程
new Thread(() -> {
while (true) {
writer.write("番茄,這裡是西紅柿,收到請回答\n");
writer.flush();
Thread.sleep(2000);
}
}).start();
// 消費者線程
new Thread(() -> {
int ch;
while ((ch = reader.read()) != -1) System.out.print((char) ch);
}).start();
注意:讀寫線程均不得退出或長時間阻塞,否則拋出 IOException: Write/Read end dead。
參考資料:《Java 編程思想》第 4 版、JDK 7 在線 API 文檔。