bilibili-Java併發學習筆記9 CountDownLatch CyclicBarrier
基於 java 1.8.0
P36_CountDownLatch使用場景與示例分析及底層源碼解讀
package java.util.concurrent;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* 一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。
*
* 用給定的計數 初始化 CountDownLatch。由於調用了 countDown() 方法,所以在當前計數到達零之前,await 方法會一直受阻塞。
* 之後,會釋放所有等待的線程,await 的所有後續調用都將立即返回。這種現象只出現一次——計數無法被重置。
* 如果需要重置計數,請考慮使用 CyclicBarrier。
*
* CountDownLatch 是一個通用同步工具,它有很多用途。將計數 1 初始化的 CountDownLatch 用作一個簡單的開/關鎖存器,或入口:在通過調用 countDown() 的線程打開入口前,
* 所有調用 await 的線程都一直在入口處等待。用 N 初始化的 CountDownLatch 可以使一個線程在 N 個線程完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待。
*
* CountDownLatch 的一個有用特性是,它不要求調用 countDown 方法的線程等到計數到達零時才繼續,而在所有線程都能通過之前,它只是阻止任何線程繼續通過一個 await。
*
* 示例用法: 下面給出了兩個類,其中一組 worker 線程使用了兩個倒計數鎖存器:
* <ul>
* <li> 第一個類是一個啓動信號,在 driver 爲繼續執行 worker 做好準備之前,它會阻止所有的 worker 繼續執行。
* <li> 第二個類是一個完成信號,它允許 driver 在完成所有 worker 之前一直等待。
* </ul>
*
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
* CountDownLatch doneSignal = new CountDownLatch(N);
*
* for (int i = 0; i < N; ++i) // create and start threads
* new Thread(new Worker(startSignal, doneSignal)).start();
*
* doSomethingElse(); // don't let run yet
* startSignal.countDown(); // let all threads proceed
* doSomethingElse();
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class Worker implements Runnable {
* private final CountDownLatch startSignal;
* private final CountDownLatch doneSignal;
* Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
* this.startSignal = startSignal;
* this.doneSignal = doneSignal;
* }
* public void run() {
* try {
* startSignal.await();
* doWork();
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }}
*
* 另一種典型用法是,將一個問題分成 N 個部分,用執行每個部分並讓鎖存器倒計數的 Runnable 來描述每個部分,
* 然後將所有 Runnable 加入到 Executor 隊列。當所有的子部分完成後,協調線程就能夠通過 await。(當線程必須用這種方法反覆倒計數時,可改爲使用 CyclicBarrier。)
*
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
* Executor e = ...
*
* for (int i = 0; i < N; ++i) // create and start threads
* e.execute(new WorkerRunnable(doneSignal, i));
*
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class WorkerRunnable implements Runnable {
* private final CountDownLatch doneSignal;
* private final int i;
* WorkerRunnable(CountDownLatch doneSignal, int i) {
* this.doneSignal = doneSignal;
* this.i = i;
* }
* public void run() {
* try {
* doWork(i);
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }}
*
* 內存一致性效果:線程中調用 countDown() 之前的操作 happen-before 緊跟在從另一個線程中對應 await() 成功返回的操作。
*
* @since 1.5
* @author Doug Lea
*/
public class CountDownLatch {
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
/**
* 構造一個用給定計數初始化的 CountDownLatch。
* @throws IllegalArgumentException 如果 count 爲負
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* 使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷。
*
* 如果當前計數爲零,則此方法立即返回。
*
* 如果當前計數大於零,則出於線程調度目的,將禁用當前線程,且在發生以下兩種情況之一前,該線程將一直處於休眠狀態:
* 由於調用 countDown() 方法,計數到達零;或者
* 其他某個線程中斷當前線程。
*
* 如果當前線程:
* 在進入此方法時已經設置了該線程的中斷狀態;或者
* 在等待時被中斷,
* 則拋出 InterruptedException,並且清除當前線程的已中斷狀態。
*
* @throws InterruptedException 如果當前線程在等待時被中斷
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
* 如果當前計數大於零,則出於線程調度目的,將禁用當前線程,且在發生以下三種情況之一前,該線程將一直處於休眠狀態:
* 由於調用 countDown() 方法,計數到達零;或者
* 其他某個線程中斷當前線程;或者
* 已超出指定的等待時間。
*
* 如果計數到達零,則該方法返回 true 值。
*
* 如果超出了指定的等待時間,則返回值爲 false。如果該時間小於等於零,則此方法根本不會等待。
*
* @param timeout 要等待的最長時間
* @param unit timeout 參數的時間單位。
* @return 如果計數到達零,則返回 true;如果在計數到達零之前超過了等待時間,則返回 false
* @throws InterruptedException 如果當前線程在等待時被中斷
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* 遞減鎖存器的計數,如果計數到達零,則釋放所有等待的線程。
*
* 如果當前計數大於零,則將計數減少。如果新的計數爲零,出於線程調度目的,將重新啓用所有的等待線程。
*
* 如果當前計數等於零,則不發生任何操作。
*/
public void countDown() {
sync.releaseShared(1);
}
/**
* 返回當前計數。
* 此方法通常用於調試和測試。
*/
public long getCount() {
return sync.getCount();
}
/**
* 返回標識此鎖存器及其狀態的字符串。狀態用括號括起來,包括字符串 "Count =",後跟當前計數。
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
package new_package.thread.p36;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(3);
IntStream.range(0, 3).forEach(i -> new Thread(() -> {
try {
Thread.sleep(i * 3000);
System.out.println("hello" + i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start());
System.out.println("啓動子線程完畢");
try {
//countDownLatch.await(3000, TimeUnit.MILLISECONDS);
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程執行完畢");
}
}