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("主线程执行完毕");
}
}