1、CountDownLatch 的作用
一種同步輔助,允許一個或多個線程等待,直到在其他線程中執行的一組操作完成。
一個通用的同步工具,可以用於多種用途。初始化一個count爲1的 CountDownLatch 作爲一個簡單的on/off閂鎖或門:所有調用 wait 的線程都在門上等待,直到調用 countDown 的線程打開它。一個初始化爲N的 CountDownLatch 可以用來讓一個線程等待,直到N線程完成某個動作,或者某個動作已經完成N次。
2、核心方法
// 構造函數,使用AQS的 status 屬性實現
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
*
* 方法描述:除非當前線程中斷,否則當前線程等待,直到鎖存器計數爲零。
* 如果當前計數爲零,則此方法立即返回。
* 如果當前線程數大於零,則當前線程將出於線程調度的目的而禁用,並處於休眠狀態,直到發生以下兩種情況之一:
* 1.調用 countDown 方法,計數爲零;
* 2.其他線程中斷當前線程。
*
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 遞減鎖存器的計數,如果計數爲零,則釋放所有等待的線程。
// 如果當前計數大於零,則遞減。如果新計數爲零,則重新啓用所有等待線程,以實現線程調度的目的。
// 如果當前計數爲零,則什麼也不會發生。
public void countDown() {
sync.releaseShared(1);
}
3、實例測試
有一個駕駛員和多個工人,駕駛員準備好以後工人纔可以開始工作,工人全部工作完以後進行整理彙報。
/**
*
* CountDownLatch用法實例(來自JDK1.8 CountDownLatch)
* 有一個駕駛員和多個工人,駕駛員準備好以後工人纔可以開始工作,工人全部工作完以後進行整理彙報。
* 對一組工作線程使用兩個倒計時鎖。
* 第一個是一個啓動信號(startSignal),阻止任何工人繼續工作,直到駕駛員準備好繼續行駛。
* 第二個信號是完成信號(doneSignal),允許駕駛員等待直到所有工人都完成。
*
* @version 1.0
*/
public class CountDownLatchDriverDemo_01 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(new CountDownLatchWorkerDemo_01(startSignal, doneSignal)).start();
}
Random random = new Random();
Integer sleepDrver = random.nextInt(1000);
Thread.sleep(sleepDrver);
System.out.println("駕駛員準備了 " + sleepDrver + " 毫秒,已經準備好了。");
/**
* 減少鎖的計數,如果計數爲零,釋放所有線程
* 如果註釋掉,線程不會繼續運行
*/
startSignal.countDown();
System.out.println("工人已經準備好了,開始工作。");
/**
* 使當前線程等待,直到鎖計數爲零
* 如果註釋掉,不會等到線程運行完就直接運行後續代碼
*/
doneSignal.await();
System.out.println("工人工作全部完成。");
}
}
class CountDownLatchWorkerDemo_01 implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
CountDownLatchWorkerDemo_01(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
Random random = new Random();
Integer sleep = random.nextInt(1000);
Thread.sleep(sleep);
System.out.println("工人" + Thread.currentThread().getId() + "開始工作,工作時間 " + sleep + "毫秒,工作完成。");
doneSignal.countDown();
} catch (Exception e) {
}
}
}