这是我参与更文应战的第 5 天,活动概况检查: 更文应战
工作中咱们必定遇到过这样的场景:“翻开多个线程分别实施不同的任务,等到全部线程的任务都实施完源码毕,然后在进行下一步的操作”。一般遇到这样的需求,咱们经过 ReentrantLock
结合 Coapprovendition
或许经过 Object
的 wait
、notify
来结束。
1. CyclicBarrier 简介
针对上述场景,在 JUC
包中现已供给了满足此类需求的 CyclicBarrier
类来结束。CyclicBarrier
的字面意思是可循环运用(Cyclic
)的屏障线程(Barrier
)。它的作用是,让一组线程抵达一个屏障(也可以叫同步点)时被堵塞,直到毕竟一个线程抵达屏障时,屏障才会开门,全部被屏障阻拦的线程才会持续干活。CyclicBarrier
默许的线程结构办法是 Cycli源码网站cBarrier(int parties)
,其参数标明屏障阻拦的线程数量,每个线程调用 await
办法告知 CyclicBarrier
我现已抵达了屏线程同步障,然后当时线程被堵塞。
示例代码
public class MyTest {
static CyclicBarrier cyclicBarrier;
public static void main(String[]args){
cyclicBarrier = new CyclicBarrier(3,new R数组和链表的区别unnable() {
@Override
public void run()数组排序 {
System.out.pr数组去重intln("悉数组织稳妥,开始登车");
}
});
for(int i=0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.print数组排序ln(Thread.curre数组去重办法ntThread().ge数组去重办法tName() + "--抵达车门");
try源码本钱 {
cyclicBarrier.await();
} catch (InterruptedExcep源码编辑器tion e) {
e.printStackTracapplee();
} catch (BrokenBarrierException e) {
e.printStackTapproverace(源码编辑器编程猫下载);
}
System.appleout.println(Thread.currentThread().getName() + "--已登车");
}
}).start();
}
}
}
实施效果:
Thread-1--抵达车门
Thread-2--抵达车门
Thread-0--抵达车门
全部安排稳妥,开始登车
Thread-0--已登车
Thread-2--已登车
Thread-1--已登车
从输出数组的日志效果可以得出实施次第,当三个线程都进行源码本钱await时分,即都抵达屏障,然后屏障翻开,各个线程接着往下实施。
场景一:将屏障的数 3
修改为 2
Thread-0--抵达approve车门
Thread-2--抵达车门appointment
全部安排稳妥,开始登车
Thread-1--抵达车门
Thread-2--已登车
Thre数组的界说ad-0--已登车
当两个线程抵达屏障时,屏障翻开源码买卖网站源码。可是为什么有个线程没上车呢?
场景二“将屏障数设置为 5
Thread-0--抵达车门数组初始化
Thread-2--抵达车门源码网站
Thread-1--抵达车门
一直无法翻开屏障,导致线程都在等候。
2. CyclicBarrier 源码解析
C源码买卖网站源码ycl数组的界说icBarrier
的源码本钱源码不多,结合上面的场景了解,它更像是一个特定场景下的东西类。
public class CyclicBarrier {
/**
* 静态内部类,当时屏障是否被损坏
*/
private static class Generation {
boolean broken = false;
}
/** 结束的Lock */
private f线程池inal ReentrantLock lock = new ReentrantLock();
/**源码编辑器编程猫下载 Condition用来结束wait */
private final Condition trip = lock.newCondition();
/** 等候的屏障数 */
private final int parties;
/* 抵达屏障要实施的Runnab源码编辑器leappearance */
private final Runnabapplicationle barrierCommand;
/** The current generation */
p源码时代rivate Generation generation = new Generation();
/**
* Numb源码网站er of parties still waiting. Counts down from parties to 0
* on each generation. It is reset to parties on each new
* gene线程池面试题ration or when broken.
*/
private int count;
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = par源码网站ties;
thisappear.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(appstoreparties, null);
}
}
从 CyclicB线程安全arrier
的成员来看,它本质上是根据 ReentrantLock
独占锁结束,经过 Lock
和 Condi源码编辑器编程猫下载tion
的结合,在加上计数器来结束。它的中心办法是 await()
。
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} ca源码之家tapproachch (TimeoutException toe) {
throw new Error(toe); // cannot happen;
}
}
/**
* Main barrier code, covering the various policies.
*/
private int dowait(boolean timed, long nanos线程池)
throws InterrAPPuptedExc线程和进程的区别是什么eption, BrokenBarrierException,
TimeoutException {
/**获取CyappearclicBaerrie数组的界说r的内部锁*/
final ReentrantLock lock = this.lock;
/**获取锁*/
lock.lock();
try {
/**存储当时的Ge线程撕裂者neration*/
final Generation g = generation;
/*源码共享网*判别当时线程同步的源码编辑器屏障是否被损坏,假定线程安全损坏则抛出BrokenBarrierException失常*/
if (g.broken)
throw new BrokenBarrierException();
/**判别当时线程是否被interrupted,假定被打断,则breakBarrier损坏屏障*/
if (数组去重Thre数组指针ad.interrupted()) {
breakBarappearrier();
throw new InterruptedException();
}
/**记载当时屏障等候个数*/
int index = --count;
if (index == 0) { // 毕竟一个预留抵达屏障的线程
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
/**实施barrierCommand指令*/
if (command != null)
command.run();
ranAction = true;
/**实施下一个Generation*/
nextGenera源码买卖网站源码tion();
return 0;
} finally数组初始化 {appstore
/**假定barrierCommand实施失利,进行屏数组的界说障损坏处理*/
if (!ranAction)
breakBarrier();
}
}
// 假定当时线程不是毕竟一个抵达的线程
for (;;) {
try {
if (!timed)///调用Condition的awai数组去重办法t()办法堵塞
trip.await();
else if (nanos > 0L)///调用Condition的awaitNanos()办法堵塞
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
/**假定当时线程被中止,则判别是否有其他线程现已使屏障损坏。若没有则进行屏障损坏处理,并抛出失常;否APP则再次中止当时线程*/
if (g == generation && !APP g.broken) {
bre数组公式akBa源码编辑器rrier();
throw ie源码;
} else {
// We're about to finish waiting even if weapp装置下载 had not
/线程和进程的区别是什么/ been interrupted, so this in数组排序te数组rrupt is deemed to
/数组c言语/ "belong" to subsequent execution.
T源码时代hr数组指针ead.currentThread(appreciate).interrupt();
}
}
if (g.broken)
throw ne数组w BrokenBarrierEx源码编辑器ception();
if (g数组去重办法 != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.u源码之家n源码之家lock();
}
}
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();
// set up next generatioappearn
count = par数组的界说ties源码本钱;
generation = new Generapp装置下载ation();
}
可以看到,中心的思维便是先判别当时实施的线程是否抵达了毕竟一个屏障,假定抵达毕竟一个屏障:“判别 barrierCommand
是否为空,不为空实施 barrierCom数组去重办法mand
任务,接着实施 nextGeneration
办法。在 nextGeneration
办法中经过 Con数组排序dition
的 signalA源码之家ll
唤醒其它堵塞的线程开始持续数组实施。”
3. 总结
经过上面的源码分析,咱们也可以得知为什么屏障翻开有个人没有上车。假定有 n
个线程,当实施到 n-1
个时,这 n-1
个都经过 Condition
的 wait
办法进行了等候。当实施到毕竟一个线程n时,则经过 Condition
的 signalAll数组词
唤醒其它堵塞的线程持续实施,一起毕竟一个线程并没有实施 wait
办法,所以也顺利实施。可是 >n
的线程则会实施了 wait
办法,毕竟没有线程唤醒,所以无法上车。