CyclicBarrier

追风远航 / 2023-08-03 / 原文

同上一遍的CountDownLatch一样,CyclicBarrier也用于控制多线程之间的同步,但比CountDownLatch稍微复杂一些,Cyclic(可循环)使用是其一大特定。

下面还是先举例其简单的使用,再按照例子分析执行流程。


最后的执行结果:

thread1 do something
thread2 do something
thread2 do something after barrier
thread2 end
thread1 end

 

CyclicBarrier类结构


里面有Condition对象,后续看代码可以发现线程的阻塞唤醒就是依赖Condition完成的。

 

一. 构造方法


有两个构造方法,一般用传入任务类的。该任务在barrier的count减到0时执行,并且哪个线程减到0哪个线程负责执行该任务。

 

二. thread1里的barrier.await()方法

三. thread2里的barrier.awaite()方法

 

private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;

if (g.broken)
throw new BrokenBarrierException();

if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}

int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}

// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// 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();

if (g != generation)
return index;

if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}



源码如上,下面比较直白的描述下做了啥。

 

thread1先加锁,count减去1还剩1(看构造函数我们传入值为2),释放锁并阻塞。

thread2加锁,count减去1为0,此时看构造函数有没有传入任务,如果有就执行该任务。唤醒所有被阻塞的线程。还原barrier(可以复用的原因)

接着执行阻塞线程后面的代码。

 

总结:

CountDownLatch可以让几个线程执行完,再执行下一个线程。

CyclicBarrier除了有上述的功能外。还有另一个重要功能,就是可以让几个线程等到同一个时间点,再一起执行。

另外CountDownLatch用完还想再用的话要重新new一个,而CyclicBarrier不用。