A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
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();
doSomethingBefore(); // 主线程先完成指定工作
startSignal.countDown(); // 主线程扣减之后,工作线程才开始工作
doSomethingElse(); //主线程做其他的工作
doneSignal.await(); // 主线程等待工作线程的完成之后才能进行后续的工作
doSomethingElse(); //主线程等待工作线程完成之后,做清尾工作
}
}
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();//工作线程开始之后,阻塞等待,直到主线程doSomethingBefore();完成之后才能继续
doWork();
doneSignal.countDown();//工作线程完成之后,扣减,所有工作线程扣减完之后,主线程才能开始清尾工作
} catch (InterruptedException ex) {} // return;
}
void doWork() { ... }
class Driver2 {
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ...
for (int i = 0; i < N; ++i) {
//创建线程执行任务
e.execute(new WorkerRunnable(doneSignal, i));
}
doneSignal.await();// 主线程等待
doSomethingElse(); //N个工作线程执行完毕之后,主线程才能继续执行
}
}
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() { ... }
}
[外链图片转存失败(img-7ySfyVm2-1565592675586)(https://note.youdao.com/yws/api/personal/file/0463F3106B184DA3B33ED23856FB1884?method=download&shareKey=db273de2b45028b93f93567b30962128)]
public CountDownLatch(int count) {
//扣除点数量不能<=0
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//await方法会调用AQS的acquireSharedInterruptibly方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
AbstractQueuedSynchronizer#acquireSharedInterruptibly
//这个方法是在AQS中实现的,是能够响应中断的获取共享状态的方法,其实就是获取state的值,
//如果state为0,就放行线程,如果大于0,线程就需要进入队列
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//这个tryAcquireShared方法就是Sync中重写的,如果state为0就会返回1,否则返回-1
//如果state是0->tryAcquireShared返回1->acquireSharedInterruptibly正常执行完毕->线程就可以放行不需要等待了
//如果state>0->tryAcquireShared返回-1->线程进入队列等待
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
//调用AQS的tryAcquireSharedNanos方法
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//AbstractQueuedSynchronizer#tryAcquireSharedNanos
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//逻辑和await类似,先判断能不能放行,如果不能就进入队列等待,doAcquireSharedNanos里面的超时逻辑也是基于tryAcquireShared来实现的
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
public void countDown() {
//会调用AQS的releaseShared方法
sync.releaseShared(1);
}
//AbstractQueuedSynchronizer#releaseShared
public final boolean releaseShared(int arg) {
//先调用tryReleaseShared方法将state减一,
//如果扣减后为0就返回true,此时会进入判断内部做一些清理工作,
//如果扣减后不为0,那么就直接返回false。这里的返回值在countDown方法里面并不关心
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
public long getCount() {
return sync.getCount();
}
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
* 同步控制CountDownLatch,使用AQS的state来表示count扣减数
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造方法,给state状态变量赋值
Sync(int count) {
setState(count);
}
//返回state的值
int getCount() {
return getState();
}
/**
*尝试获取共享状态变量,如果state是0就返回1,反之大于0就返回-1
*是await实现的基础
*/
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
/**
* 该方法是countDown方法实现的基础,
* 方法内部通过CAS来将state减一
* 返回false表示state数在被扣减后还大于0,
* 返回true表示state为0已经没有扣减数了
*/
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;
}
}
}
synchronization aid that allows a set of threads to all wait for
* each other to reach a common barrier point. CyclicBarriers are
* useful in programs involving a fixed sized party of threads that
* must occasionally wait for each other. The barrier is called
* cyclic because it can be re-used after the waiting threads
are released.
class Solver {
final int N;
final float[][] data;
final CyclicBarrier barrier;
//每个工作线程用来处理数组的每一行,
class Worker implements Runnable {
int myRow;
Worker(int row) { myRow = row; }
public void run() {
//1.如果没有结束就处理,因为有可能某一个线程还没有处理,就已经找到结果不需要处理了,因此这里先判断一下
while (!done()) {
processRow(myRow);
//2.处理完之后,线程需要等待,等其他并行处理某一行的线程一起处理完
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
//定义一个barrierAction,这个线程是在所有的Worker都到达栅栏之后,才会执行mergeRows合并结果的操作
Runnable barrierAction = new Runnable() { public void run() { mergeRows(...); }};
barrier = new CyclicBarrier(N, barrierAction);
List threads = new ArrayList(N);
for (int i = 0; i < N; i++) {
Thread thread = new Thread(new Worker(i));
threads.add(thread);
thread.start();
}
//等待工作线程
for (Thread thread : threads)
thread.join();
}
}
初始化data完毕...
线程:Thread-0处理第0行第0列...
线程:Thread-1处理第1行第0列...
线程:Thread-2处理第2行第0列...
线程:Thread-3处理第3行第0列...
线程:Thread-4处理第4行第0列...
线程:Thread-5处理第5行第0列...
线程:Thread-6处理第6行第0列...
线程:Thread-7处理第7行第0列...
线程:Thread-8处理第8行第0列...
线程:Thread-9处理第9行第0列...
MergeRows 开始Merge...Thread-9
MergeRows 完毕耗费3000ms ,Thread-9
MergeRows 失败 ,结果是: 455.0
线程:Thread-9处理第9行第1列...
线程:Thread-0处理第0行第1列...
线程:Thread-2处理第2行第1列...
线程:Thread-1处理第1行第1列...
线程:Thread-4处理第4行第1列...
线程:Thread-5处理第5行第1列...
线程:Thread-6处理第6行第1列...
线程:Thread-7处理第7行第1列...
线程:Thread-8处理第8行第1列...
线程:Thread-3处理第3行第1列...
MergeRows 开始Merge...Thread-3
MergeRows 完毕耗费3000ms ,Thread-3
MergeRows 失败 ,结果是: 837.0
线程:Thread-3处理第3行第2列...
线程:Thread-9处理第9行第2列...
线程:Thread-0处理第0行第2列...
线程:Thread-1处理第1行第2列...
线程:Thread-4处理第4行第2列...
线程:Thread-6处理第6行第2列...
线程:Thread-8处理第8行第2列...
线程:Thread-2处理第2行第2列...
线程:Thread-7处理第7行第2列...
线程:Thread-5处理第5行第2列...
MergeRows 开始Merge...Thread-5
MergeRows 完毕耗费3000ms ,Thread-5
MergeRows 失败 ,结果是: 1244.0
线程:Thread-5处理第5行第3列...
线程:Thread-3处理第3行第3列...
线程:Thread-9处理第9行第3列...
线程:Thread-0处理第0行第3列...
线程:Thread-4处理第4行第3列...
线程:Thread-1处理第1行第3列...
线程:Thread-8处理第8行第3列...
线程:Thread-2处理第2行第3列...
线程:Thread-6处理第6行第3列...
线程:Thread-7处理第7行第3列...
MergeRows 开始Merge...Thread-7
MergeRows 完毕耗费3000ms ,Thread-7
MergeRows 失败 ,结果是: 1815.0
线程:Thread-7处理第7行第4列...
线程:Thread-5处理第5行第4列...
线程:Thread-9处理第9行第4列...
线程:Thread-8处理第8行第4列...
线程:Thread-3处理第3行第4列...
线程:Thread-6处理第6行第4列...
线程:Thread-2处理第2行第4列...
线程:Thread-1处理第1行第4列...
线程:Thread-0处理第0行第4列...
线程:Thread-4处理第4行第4列...
MergeRows 开始Merge...Thread-4
MergeRows 完毕耗费3000ms ,Thread-4
MergeRows 成功 ,结果是: 2188.0
if (barrier.await() == 0) {
// 合并过程逻辑执行...
}
AAA
await()
BBB
按照优先级理解为:AAA > barrierAction > BBB
1.最后一个线程到达栅栏
2.该线程被中断(即线程的interrupt方法被其他线程调用)
3.其他等待栅栏的线程被中断
4.其他等待栅栏的线程超时释放
5.栅栏的reset()方法被调用
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException,TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException,TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//Generation是定义的一个内部类,这个类极其简单,就只包含一个boolean类型的属性代表当前栅栏是否被破坏,false表示没有被破坏,true表示被破坏了。
//我们在前面提到过栅栏是可以重复使用的,我们的示例1的实现也佐证了这一点,每一次使用栅栏,都相对应于一个Generation实例,后面我们看源码对Generation
//的源码注释进行解读。这个Generation是不需要我们通过构造方法实例化的,我们每实例化一次CyclicBarrier,里面都有一个Generation
//对于CyclicBarrier,我们看方法图可以知道,可以操作的api主要就是await方法,其余的几个方法基本上是读方法,就是用来获取关于CyclicBarrier
//内部信息的,因此每一次await
final Generation g = generation;
//2.如果栅栏被破坏旧抛出异常
if (g.broken)
throw new BrokenBarrierException();
//3.如果线程被中断,就破坏栅栏,并抛出中断异常
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//count代表当前还没有到达栅栏的线程,既然当前线程调用了await,说明它已经到栅栏了,因此count减一
int index = --count;
//如果减一之后为0,说明当前线程是最后一个了,
if (index == 0) { // tripped
boolean ranAction = false;
try {
//如果构造方法传入了barrierAction,就让barrierAction执行,(barrierCommand就是barrierAction)
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
//看上面的逻辑可知,如果构造方法没有传入barrierAction,说明没有barrierAction需要执行,那么就需要放行全部的线程,此时会进入下面这个
//if逻辑,这时候就会打破栅栏,在breakBarrier方法里面会将generation.broken = true表示栅栏已经破坏,同时初始化count为初始值,并且唤
//醒全部的其他线程,(其实这里比较形象,就好比几个人约定达到栅栏后再一起进行后续的工作,现在最后一个人到了,他就打破栅栏并唤醒其他伙伴)
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
//如果index不是0,很简单,说明自己并不是最后一个线程,此时应该阻塞等待后面的线程,由最后面的线程来负责后面的
//指挥(要么调用barrierAction,要么唤醒大家一起执行后面的步骤)
for (;;) {
//在于一个for循环里面自旋,
try {
if (!timed)
//如果不是超时模式,那么就直接在Condition上await即可,此时会释放锁,然后后面的伙伴可能会进来执行这个代码块
//这里需要掌握的一点是,调用await方法会自动释放锁,然后再次往下执行就会获得锁,这个过程是JVM保证的,非常基础且重要的知识点
//在前面使用Lock对象加锁之后,调用Lock生成的condition对象的await是会将锁释放的,
//这里再补充一点,如果直接调用lock的wait是会抛出IllegalMonitorStateException,因为我们的锁对象根本不是lock这个对象
trip.await();
else if (nanos > 0L)
//如果是超时模式,那么就在Condition上执行支持超时的await
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//阻塞的线程被打断,我们前面有写过,需要打破栅栏,而且自己会抛出异常
if (g == generation && ! g.broken) {
//如果栅栏的标志是false(false表示没有打破),那么就打破栅栏,抛出异常
breakBarrier();
throw ie;
} else {
//走到这逻辑说明g的broken状态是true,说明栅栏已经打破了,就不需要我们关心了,中断自己即可(管好自己就行啦)。
//因为不自己中断自己的话,自己还会继续运行的,捕获异常后中断标志位会被复位(不了解这个的需要补补啦)
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
//等待过程中,如果被唤醒的话,就会执行到这里,还要检查栅栏是否被打破,打破的话抛异常
if (g.broken)
throw new BrokenBarrierException();
//这个条件说明generation被更新了,前面一点点在执行完barrierAction会更新generation,这里再次验证了之前的说法,一次栅栏的使用对
//应一个generation对象,运行到此处,其实说明在自己等待的过程中(自己不是最后的线程),最后的线程跑到了栅栏点,并且更新了generation
//对象,就好比最后一个线程宣布,这一次我们可以继续执行了,我们需要一起跑到下一个栅栏(不管有没有下一个栅栏,反之这次结束了),
//因此本线程也不需要再傻傻等待了,直接返回,返回值是代表当前线程跑到栅栏的顺序(如果是5个线程,那么第一个到的线程返回4,第二个返回3,,最后一个返回0)
if (g != generation)
return index;
//如果执行到这里,说明最后一个线程还没到栅栏(它到的话,要么会更新generation,要么打破栅栏,不管哪一种,在前面2个判断就会返回了)
//那么就检查是否超时,超时就打破栅栏,抛异常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
public int getNumberWaiting() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return parties - count;
} finally {
lock.unlock();
}
}
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
对比项 | CountDownLatch | CyclicBarrier |
---|---|---|
原理 | 基于AQS来实现 | 内部使用Condition+CAS来实现(dowait里面的for死循环可以理解为CAS) |
实现细节 | 使用AQS的state来代表扣除点,await和countDown对应state的判断和原子自减 | 使用锁机制,由最后到达线程来确定整组线程的放行,被阻塞的线程自旋等待 |
特点 | 不可重复使用,扣减不可逆 | 可循环使用,每次使用通过内部的Generation对象来代表当前该次使用期间栅栏的状态(最后达到栅栏的线程会负责更新Generation对象) |
应用场景 | 一个或者一组线程的执行由其他线程来控制,比如发令枪,让所有线程阻塞在一个地方,由外部线程coutDown放行 | 一组线程的放行由该组线程内部决定,比如多个线程协作分阶段完成任务,每个阶段可以做同步等待 |