CyclicBarrier
There is a similar fence, meaning that before you open the fence can only be blocked on one side of the fence, when the fence is removed, before being blocked on one side of the plurality of objects is simultaneously started to move up.
1. How to use CyclicBarrier
Before introducing the principle, first look at CyclicBarrier
how it should be used.
Suppose now that there is such a scenario, we need to open a meeting , you need Zhang 1, 2 Zhang, Zhang 3 three people to attend,
the meeting after three people were in attendance to begin, or only dry waiting; this scene with CyclicBarrier
can be very fit simulated. code show as below:
public static void main(String[] args) {
// 线程池,每个线程代表一个人
ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
// 会议所需的人数为3
CyclicBarrier barrier = new CyclicBarrier(3);
executor.execute(() -> {
try {
System.err.println("张1到达会议室");
barrier.await();
System.err.println("会议开始,张1开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张2到达会议室");
barrier.await();
System.err.println("会议开始,张2开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张3先去个厕所,内急解决再去开会");
TimeUnit.SECONDS.sleep(1);
System.err.println("张3到达会议室");
barrier.await();
System.err.println("会议开始,张3开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.shutdown();
}
Results Figure:
through the top of the code may know CyclicBarrier
a few points:
- Use
await()
to indicate completion of certain things. (Example above performance reached the conference room ) - Use
await()
after the current thread enters the blocked state , need to wait to fully meetCyclicBarrier
the conditions of the wake to continue the next operation. (In the example above three individuals have reached the conference room ) - After the last thread to achieve the condition before blocking all open threads continue to the next operation. (The above example is Zhang reach 3 conference room)
This simple example also let us know CyclicBarrier
using the method, and that is to look at its internal exactly how to achieve the effect of the fence.
2. CyclicBarrier How is a "fence" of
We can see from the first code, we need to focus on two places
- Constructor
- await () method
As long as these two methods to understand the internal, which is equivalent to understand the CyclicBarrier
interior.
Before delving into that, first look at CyclicBarrier
several variables, do not have to remember, when looking at the code to know what to do with this thing on the line:
lock:
CyclicBarrier
class creates theReentrantLock
instance, aboutReentrantLock
unclear can -> transfer.trip:
lock
incondition
,CyclicBarrier
use the variable to achieve blocking and wake up at the same time among the threads . Similarly, no clearcondition
action => Portal .parties: the need to satisfy conditions (call
await
the number of ways), and that is when there are parties threads await () wakes up all the threads.barrierCommand: a
Runnable
variable in theawait
total number of calls to the method of arrivingparties
after, before the wake all the threads execute itsrun()
methodgeneration: its inner classes can be understood as a period, complete the cycle n tasks, as long as a task fails, all of the tasks of the current period even if the failure to end the current cycle, and then turn the next cycle.
count: the current period the number of tasks remaining to be done (the remaining call
await
number method)
The following is the source:
public class CyclicBarrier {
// 内部类,可理解为周期
private static class Generation {
// 当前周期是否失败
boolean broken = false;
}
// 锁的实例
private final ReentrantLock lock = new ReentrantLock();
// ReentrantLock的condition变量,用来控制线程唤醒和阻塞
private final Condition trip = lock.newCondition();
// 需要满足条件的次数,即需要调用await方法的次数
private final int parties;
// 满足条件次数达到parties之后,唤醒所有线程之前执行其 run()方法
private final Runnable barrierCommand;
// 当前周期
private Generation generation = new Generation();
// 剩余满足条件次数
private int count;
// ...
}
Read CyclicBarrier
after several variables, look at its internal implementation details.
First look constructor , which has two constructors, a wake-up directly after reaching the total number of all threads (parties) condition ; another designated a Runnable
first execute after reaching its run conditions Total () method and then wake up.
- Do not specify
Runnable
, only one parameter: the need to reach a number of tasks
public CyclicBarrier(int parties) {
// 直接调用另一个构造方法,Runnable传null,表示不执行
this(parties, null);
}
- Specifies the
Runnable
constructor, assignment total number of tasks, the number of remaining tasks, wake up before operationRunnable
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
// 任务总数
this.parties = parties;
// 剩余需要完成的任务数
this.count = parties;
// 唤醒之前执行的Runnable
this.barrierCommand = barrierAction;
}
In the first section we use the first constructor, to try the second
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
/** =======增加Runnable,其他地方保持一致=============*/
CyclicBarrier barrier = new CyclicBarrier(3, ()-> System.err.println("在会议开始之前,先给大家发下开会资料"));
executor.execute(() -> {
try {
System.err.println("张1到达会议室");
barrier.await();
System.err.println("会议开始,张1开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张2到达会议室");
barrier.await();
System.err.println("会议开始,张2开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.execute(() -> {
try {
System.err.println("张3先去个厕所,内急解决再去开会");
TimeUnit.SECONDS.sleep(1);
System.err.println("张3到达会议室");
barrier.await();
System.err.println("会议开始,张3开始发言");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
executor.shutdown();
}
Results Figure:
Reading the constructor, even if appreciated half CyclicBarrier
, the other half of the next term - ; await()
tracking code, this is seen
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
Call direct dowait
method for the mass participation false
and 0 , which means unlimited waiting, unless the thread is interrupted or wake . Re-entry dowait
method, which is the CyclicBarrier
other half, in the code below clearly written in the entire execution process
/** 参数说明, timed:是否限时, nanos:限时时间*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException, TimeoutException {
// 锁
final ReentrantLock lock = this.lock;
// 获取锁,如果失败的话线程睡眠,进入同步队列(AQS中的知识)
lock.lock();
try {
/* 拿到锁之后进入代码处理逻辑*/
// 当前周期
final Generation g = generation;
// 如果当前周期是失败的,那么直接抛错
if (g.broken)
throw new BrokenBarrierException();
// 如果当前线程被打断了,那么此次周期失败,设置相关参数,然后抛错
if (Thread.interrupted()) {
// 实现代码在下行的注释中,设置相关参数来提醒其他线程周期失败了
breakBarrier();
/*
* private void breakBarrier() {
* generation.broken = true;
* count = parties;
* // 唤醒condition中的所有线程
* trip.signalAll();
* }
*/
throw new InterruptedException();
}
// 如果成功了,那么剩余任务数(count)减1
int index = --count;
// 如果为0则表示达到剩余的任务数没有了,达到CyclicBarrier的条件总数了,需要唤醒其他线程
if (index == 0) {
boolean ranAction = false;
try {
// 唤醒之前的Runnable
final Runnable command = barrierCommand;
// 如果不为空的话执行其run方法
if (command != null)
command.run();
ranAction = true;
// 开启下个周期,这个方法是CyclicBarrier可以复用的原因,具体实现在下行注释
nextGeneration();
/* private void nextGeneration() {
* // 首先叫醒当前周期的其他线程,告诉其周期结束了,可以执行接下来的操作了
* trip.signalAll();
* // 然后开启下个周期,剩余任务数重置
* count = parties;
* // 下个周期
* generation = new Generation();
* }
*/
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 如果还不能结束本周期,就一直等待直到结束或者周期失败
for (;;) {
try {
// await的过程中是释放锁的
// 不限时的话就一直等待直到被唤醒或者打断
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();
}
}
Here on the basic understanding of CyclicBarrier
the internal realized, like other arguments await
it is the same logic, but is more limited access only.
In fact, if you know ReentrantLock
, then you know that CyclicBarrier
the whole is on ReentrantLock
the condition
utilization of it.
3. Summary
Overall CyclicBarrier
implementation is relatively simple, said that ReentrantLock
the condition
upgraded version is not excessive. The key points of two, for a constructor, before deciding the number of tasks and wake-up operations ; Another point await
method, each time under normal circumstances await
would reduce a number of tasks ( the total number determined by the constructor ), the number of tasks time becomes zero indicates the end of the cycle , need to wake up condition
other threads, but on the way encounter failure, then the current cycle fails, wake up other threads together throwing error.
Failure does not make you weak, fear of failure will be.