多線程協作是 Java 並發編程的核心課題。本文通過 5 個經典場景,系統講解 Thread.join()、Object.wait/notify、CountDownLatch、CyclicBarrier、FutureTask + Callable 等核心機制的原理、適用場景與代碼實戰,助你構建高效、可控的並發系統。
需求:線程 B 等待線程 A 完全結束後再執行。
Thread A = new Thread(() -> printNumber("A"));
Thread B = new Thread(() -> {
try { A.join(); } catch (InterruptedException e) { e.printStackTrace(); }
printNumber("B");
});
B.start(); A.start(); // B 先啟動但會阻塞等待 A
原理:join() 內部調用 wait(0),當線程終止時 JVM 自動喚醒等待者。適用於簡單的串行依賴。
需求:A 印 1 → B 印 1,2,3 → A 印 2,3。
Object lock = new Object();
Thread A = new Thread(() -> {
synchronized(lock) {
System.out.println("A 1");
lock.wait(); // 釋放鎖進入等待
System.out.println("A 2"); System.out.println("A 3");
}
});
Thread B = new Thread(() -> {
synchronized(lock) {
System.out.println("B 1"); System.out.println("B 2"); System.out.println("B 3");
lock.notify(); // 喚醒 A
}
});
A.start(); B.start();
關鍵點:
lock。wait() 釋放鎖並進入 WAITING 狀態;notify() 喚醒一個等待線程(需重新競爭鎖)。synchronized 區塊內調用,否則拋 IllegalMonitorStateException。需求:D 等待 A、B、C 全部結束(A/B/C 並行執行)。
CountDownLatch latch = new CountDownLatch(3); // 計數器初值 3
// D 線程
new Thread(() -> {
latch.await(); // 阻塞直到計數歸零
System.out.println("D start");
}).start();
// A/B/C 線程
for (char n : new char[]{'A','B','C'}) {
new Thread(() -> {
doWork(n);
latch.countDown(); // 計數 -1
}).start();
}
特點:一次性計數器,不可重置。適用於「主線程等待多個任務完成」或「多線程等待準備就緒」。
需求:三選手各自準備,全員就緒後同時起跑。
CyclicBarrier barrier = new CyclicBarrier(3); // 3 方參與
for (char n : new char[]{'A','B','C'}) {
new Thread(() -> {
prepare(n);
barrier.await(); // 到達屏障等待
run(n); // 所有人同時執行
}).start();
}
對比 CountDownLatch:
| 特性 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 可重用性 | 不可重置 | 可循環使用(reset()) |
| 等待邏輯 | 一等多 | 多等多(互相等待) |
| 攔截動作 | 無 | 可選 barrierAction(所有方到達前執行) |
Runnable 無返回值,Callable<V> 支持泛型返回值與拋出異常。
Callable<Integer> task = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum;
};
FutureTask<Integer> future = new FutureTask<>(task);
new Thread(future).start();
System.out.println("主線程繼續做其他事...");
Integer result = future.get(); // 阻塞獲取結果
System.out.println("結果:" + result); // 5050
進階:生產環境建議配合 ExecutorService 線程池管理 Future,避免手動創建線程開銷,並可使用 CompletableFuture (Java 8+) 實現非阻塞回調組合。
Thread.join()wait/notify(或 Lock + Condition 更靈活)CountDownLatchCyclicBarrierFutureTask/Callable → 升級 CompletableFuture線程通信本質是「狀態協調與內存可見性」。掌握這些工具的適用邊界,能寫出既正確又高效的並發代碼。