首页 国际新闻正文

银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉

原文出处:http://cmsblogs.com/

作者:chenssy

此篇博客一切源码均来自JDK 1.8

CyclicBarrier,一个同步辅佐类,在API中是这么介绍的: 它答应一组线程相互等候,直到抵达某个公共屏障点 (common barrier point)。在触及一组固定巨细的线程的程序中,这些曼若姿线程有必要不时地相互等候,此刻 CyclicBarrier 很有用。由于该 barrier 在开释等候线程后能够重用,所以称它为循环 的 barrier。 浅显点讲便是:让一组线程抵达一个屏障时被堵塞,直到最终一个线程抵达屏障时,屏障才会开门,一切被屏障阻拦的线程才会持续干活。

完成剖析

CyclicBarrier的结构如下:

经过上图咱们能够看到CyclicBarrier的内部是运用重入锁ReentrantLock和Condition。它有两个结构函数:

  • CyclicBarrier(int parties):创立一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等候状况时发动,但它不会在发动 barrier 时履行预界说的操作。
  • CyclicBarrier(int parties, Ru竹骨绸伞nnable barrierAction) :创立一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等候状况时发动,并在发动 barrier 时履行给定的屏障操作,该操作由最终一个进入 barrier 的线程履行。

parties表明阻拦线程的数量。 barrierAction 为CyclicBarrier接纳的Runnable指令,用于在线程抵达屏障时,优先履行barrierAction ,用于处理愈加杂乱的事务场景。

 public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.老陈敬说barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}

在CyclicBarrier中最重要的办法莫过于await()办法,在一切参与者都现已在此 barrier 上调用 await 办法之前郭小美,将一向等候。如下:

 public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);//不超时等候
} catch (TimeoutExceptio银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉n toe) {
throw new Error(toe); // cannot happen
}
}

await()办法内部调用dowait(bo涩涩撸olean timed, long nanos)办法:

 private int dowait(boo银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉lean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
//获取锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//分代
final Generation g = generation;
//当时generation“已损坏”,抛出BrokenBarrierException反常
//抛出该反常一般都是某个线程在等候某个处于“断开”状况的CyclicBarrie
if (g.broken)
//当某个线程企图等候处于断开状况的 barrier 时,或许 barrier 进入断开状况而线程处于等候状况时,抛出该反常
throw new BrokenBarrierException();
//假如线程中止,停止CyclicBarrier
if (Thread.interrupted()) {
breakBarrier();
throw new Interrupted球王开荒纪Exception();
}
//进来一个线程 count - 1
int index = --count;
//count ==银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉 0 表明一切线程均已到位,触发Runnable使命
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
//触发使命
if (command != null)
command.run();
ranAction = true;
//唤醒一切等候线程,并更新generation
nextGyls官网ene尼玛拉姆ration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
for (;;) {
try {
//假如不是超时等候,则调用Condition.await()办法等候
if (!timed夏天树莓蛋糕)
trip.await();
else if (nanos > 0L)
//超时等候,调用Condition.awai重生人鱼倾全国tNanos()办法等候
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 thi银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉s interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
//generation现已更新,回来index
if (g != generation)
return index;
//“超时等候”,而且时刻已到,停止CyclicBarrier,并抛出反常
if (timed && nano离央s <= 0L) {
breakBarrier();
throw new TimeoutExcept阿福宝盒ion();
}
}
} finally {
//开释锁
lock.unlock();
}
}

其实await()的处理逻辑仍是比较简单的:假如该线程不是抵达的最终一个线程,则他会一向处于等候状况,除非发作以下状况:

  1. 最终一个线程抵达,即index == 0
  2. 超出了指定时刻(超时等候)
  3. 其他的某个线程中止当时线程huoyrz
  4. 其他的某个线程中止另一个等候的线程
  5. 其他的某个线程在等候barrier超时
  6. 其他的某个线程在此barrier调用reset()办法。reset()办法用于将屏障重置为初始状况。

在娇宠权后上面的源代码中,咱们或许需求留意Generation 目标,在上述代码中咱们总是能够看到抛出BrokenBarrierException反常,那么什么时候抛出反常呢?假如一个线程处于等候状况时,假如其他线程调用reset(),或许调用的barrier本来便是被损坏的,则抛出BrokenBarrierException反常。一起,任何线程在等候时被中止了,则其他一切线程都将抛出Bro肥肥的女儿kenBarrierException反常,并将barrier置于损坏状况。 一起,Generation描绘着CyclicBarrier的更显换代。在CyclicBarrier中,同一批线程归于同一代。当有parties个线程抵达barrier,generation就会被更新换代。其间broken标识该当时CyclicBarrier是否现已处于中止状况。

 pr收回高铬砖ivate static class Generation {
boolean broken = false;
}

默许barrier是没有损坏的。 当barrier损坏了或许有一个线程中止了,则经过breakBarrier()来停止一切的线程:

 private void breakBarrier() {
李秉洁generati耐组词on.broken = true;
count = parties;
trip.signalAll();
}

在breakBarrier()中除了将broken设置为true,还会调用signalAll将在CyclicBarrier处于等候状况的线程悉数唤醒。 当一切线程都现已抵达barrier处(index == 0),则会经过nextGeneration()进行更新换地操作,在这个过程中,做了三件事:唤醒一切线程,重置count,generation。

 private void nextGenera银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉tion() {
trip.signalAll();
count = parties;
generation = new Generation();
}

CyclicBarrier一起也供给了await(long timeout, TimeUnit unit) 办法来做超时操控,内部仍是经过调用doawait()完成的。

使用银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉场景

CyclicBarrier试用与多线程成果兼并的操作,用于多线程核算数据,最终兼并核算成果的使用场景。比银河系,「死磕Java并发」—–J.U.C之并发东西类:CyclicBarrier,我的安吉拉如咱们需求核算多个Excel中的数据,然后比及一个总成果。咱们能够经过多线程处理每一个Excel,履行完成后得到相应的成果,最终经过barrierAction来核算这些线程的核算成果,得到一切Excel的总和。

使用示例

比方咱们开会只要等一切的人到齐了才会开会,如下:

运转成果:

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。