CountDownLatch 通過一個計數器實現,初始值為線程數量。每個線程完成任務後調用 countDown() 使計數器減 1,當計數器歸零時,在 await() 处等待的線程恢復執行。
核心方法:
public CountDownLatch(int count):構造函數,count 為需等待的線程數,只能設置一次,無重置機制。public void countDown():計數器減 1,通知機制由此完成。public void await() throws InterruptedException:當前線程阻塞,直到計數器為 0。
package com.test;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String args[]) throws Exception {
CountDownLatch latch = new CountDownLatch(3);
Worker worker1 = new Worker("Jack 程序員1", latch);
Worker worker2 = new Worker("Rose 程序員2", latch);
Worker worker3 = new Worker("Json 程序員3", latch);
worker1.start();
worker2.start();
worker3.start();
latch.await(); // 主線程阻塞
System.out.println("Main thread end!");
}
static class Worker extends Thread {
private String workerName;
private CountDownLatch latch;
public Worker(String workerName, CountDownLatch latch) {
this.workerName = workerName;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println("Worker:" + workerName + " is begin.");
Thread.sleep(1000L);
System.out.println("Worker:" + workerName + " is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown(); // 任務完成,計數器-1
}
}
}
運行結果:
Worker:Rose 程序員2 is begin.
Worker:Json 程序員3 is begin.
Worker:Jack 程序員1 is begin.
Worker:Jack 程序員1 is end.
Worker:Json 程序員3 is end.
Worker:Rose 程序員2 is end.
Main thread end!
主線程在 latch.await() 處阻塞,直到三個工作線程全部完成。
場景: 大型任務拆分為多個子任務,所有子任務完成後才能執行主任務。
package com.test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String args[]) throws Exception {
CyclicBarrier barrier = new CyclicBarrier(3, new TotalTask());
BillTask worker1 = new BillTask("111", barrier);
BillTask worker2 = new BillTask("222", barrier);
BillTask worker3 = new BillTask("333", barrier);
worker1.start();
worker2.start();
worker3.start();
System.out.println("Main thread end!");
}
static class TotalTask extends Thread {
public void run() {
System.out.println("所有子任務都執行完了,就開始執行主任務了。");
}
}
static class BillTask extends Thread {
private String billName;
private CyclicBarrier barrier;
public BillTask(String workerName, CyclicBarrier barrier) {
this.billName = workerName;
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println("市區:" + billName + "運算開始:");
Thread.sleep(1000L);
System.out.println("市區:" + billName + "運算完成,等待中...");
barrier.await(); // 等待所有線程到達
System.out.println("全部都結束,市區" + billName + "才開始後面的工作。");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
運行結果:
市區:111運算開始:
市區:333運算開始:
Main thread end!
市區:222運算開始:
市區:333運算完成,等待中...
市區:222運算完成,等待中...
市區:111運算完成,等待中...
所有子任務都執行完了,就開始執行主任務了。 // 最後到達await的線程執行
全部都結束,市區111才開始後面的工作。
全部都結束,市區222才開始後面的工作。
全部都結束,市區333才開始後面的工作。
關鍵點: 構造 CyclicBarrier 時傳入 Runnable 實現類(TotalTask),所有線程到達 await() 後一起繼續,最後到達的線程執行 TotalTask.run()。若無傳入 Runnable,則直接一起返回繼續執行。
| 特性 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 作用 | 1或N個線程等待其他線程完成 | N個線程相互等待 |
| 計數器重置 | 不可重置 | 可重置(循環使用) |
Java Semaphore 用於線程間傳遞信號,維護一組許可。概念上對可用許可計數,常用於資源池(如數據庫連接池)。
場景: 服務器資源有限,同一時刻僅允許 3 人訪問,共 10 人請求。
package com.test;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String args[]) throws Exception {
final Semaphore semaphore = new Semaphore(3); // 僅允許3個許可
for (int i = 0; i < 10; i++) {
final int no = i;
Runnable thread = new Runnable() {
public void run() {
try {
System.out.println("用戶" + no + "連接上了:");
Thread.sleep(300L);
semaphore.acquire(); // 獲取許可
System.out.println("用戶" + no + "開始訪問後台程序...");
Thread.sleep(1000L); // 模擬訪問
semaphore.release(); // 釋放許可
System.out.println("用戶" + no + "訪問結束。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(thread).start();
}
System.out.println("Main thread end!");
}
}
運行結果:
用戶1連接上了:
用戶3連接上了:
用戶4連接上了:
用戶2連接上了:
用戶0連接上了:
用戶5連接上了:
用戶7連接上了:
Main thread end!
用戶6連接上了:
用戶8連接上了:
用戶9連接上了:
用戶3開始訪問後台程序...
用戶4開始訪問後台程序...
用戶2開始訪問後台程序...
用戶4訪問結束。
用戶3訪問結束。
用戶7開始訪問後台程序...
用戶0開始訪問後台程序...
用戶8開始訪問後台程序...
用戶2訪問結束。
用戶5開始訪問後台程序...
用戶0訪問結束。
用戶7訪問結束。
用戶1開始訪問後台程序...
用戶8訪問結束。
用戶6開始訪問後台程序...
用戶1訪問結束。
用戶9開始訪問後台程序...
用戶5訪問結束。
用戶6訪問結束。
用戶9訪問結束。
10 個線程同時啟動,但始終只有 3 個線程能同時獲得許可訪問資源,釋放一個進來一個。
與前三種基於計數器的同步機制不同,Exchanger 用於成對出現的線程之間交換數據(兩個線程共享一個 Exchanger)。