| 特性 | Object.wait() | Thread.sleep() |
| 所屬類 | Object | Thread |
| 鎖行為 | 釋放鎖,進入等待隊列 | 持有鎖,僅讓出 CPU |
| 喚醒方式 | notify/notifyAll (同一鎖對象) | 時間到期自動喚醒 |
| 前提條件 | 必須在 synchronized(lock) 塊中 | 無 |
| 典型用途 | 線程間協作 (生產消費者) | 定時暫停、重試延遲 |
面試題:三線程交替打印 ABC (各 10 次)
題目要求
建立三個線程 A/B/C,每個打印 10 次對應字母,要求併發運行、嚴格交替輸出 ABCABC... (共 30 次)。
核心思路:鏈式鎖傳遞
- 每個線程持有兩把鎖:自己的鎖
self 與前驅線程的鎖 prev。
- 線程啟動後嘗試同時獲取
prev 和 self (按固定順序避免死鎖)。
- 獲得雙鎖後打印一次,
prev.notify() 喚醒前驅線程,釋放 prev,在 prev.wait() 等待被後繼線程喚醒。
- 形成環狀:A 等 C、B 等 A、C 等 B。
完整代碼
public class PrintABC implements Runnable {
private final String name;
private final Object prev; // 前驅鎖
private final Object self; // 自身鎖
public PrintABC(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
// self.notify(); // 此處不需要 notify self
}
try {
prev.wait(); // 等待前驅喚醒 (第一輪除外)
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object a = new Object(), b = new Object(), c = new Object();
PrintABC taskA = new PrintABC("A", c, a); // A 等 C
PrintABC taskB = new PrintABC("B", a, b); // B 等 A
PrintABC taskC = new PrintABC("C", b, c); // C 等 B
// 必須按順序啟動,確保初始鎖狀態正確
new Thread(taskA).start();
Thread.sleep(10); // 微小延遲確保啟動順序
new Thread(taskB).start();
Thread.sleep(10);
new Thread(taskC).start();
}
}
關鍵點解析
- 雙鎖順序:先鎖
prev 再鎖 self,所有線程一致,避免死鎖。
- 首輪啟動:主線程依次
start() 並 sleep(10),確保 A 先持有 c&a、B 持有 a&b、C 持有 b&c,打印第一輪 ABC 後各自 wait() 釋放 prev。
- 喚醒傳遞:A 打印完
wait() 在 c 上;C 打印完在 synchronized(c) 隱式釋放鎖時喚醒 A (JVM 保證)。同理 B←A、C←B。
- 輸出結果:
ABCABCABCABCABCABCABCABCABCABC (30 字符)。
進階優化
- 用
LockSupport.park/unpark 替代 wait/notify,無需 synchronized,語義更清晰。
- 用
Phaser / CyclicBarrier 實現多方同步。
- 用
CompletableFuture 串聯任務。
總結
掌握 Runnable 優於 Thread、start() 啟動併發、wait 釋放鎖 vs sleep 持有鎖、鏈式雙鎖實現線程有序交替,是 Java 多線程核心基本功。實戰中優先使用 java.util.concurrent 高級組件 (Lock、Condition、Phaser、CompletableFuture) 降低復雜度、提高可靠性。
- 上一篇文章: jQuery Pagination 分頁插件增強版:新增首頁/尾頁/頁碼跳轉功能完整實戰
- 下一篇文章: JCrop 與 ajaxUpload 實現圖片裁切上傳完整教程
Copyright ©
程式碼之家 All Rights Reserved