Learning Java Concurrency - CyclicBarrier
CyclicBarrier,正如同名字里面表达出来的,是一个可以循环使用的屏障。与CountDownLatch类似,它可以用来表达等待某个状态,比如大家都到齐了,那么开始开会吧。与CountDownLatch不同的是,它可以多次等待,也就是可以等待多个状态。
好吃不过栗子。比如哈利波特的三强争霸赛,要一项一项任务地完成,大家都结束了(不管成功还是失败),才开始计划下一个任务。第一个是去挑战龙;然后是到海里面挑战人鱼群;最后是挑战伏地魔。每一个任务总是要等大家都完成了才开始。
还有一个栗子。大家一起去面试,有的公司为了省事,等凑齐了一波人才开始走流程。HR领着大家一起先笔试,然后安排初面官,然后安排复试官。每一关刷掉一波人,但是只有大家都结束了才进行下一轮。别问我哪里有这样的招聘部门,人家开心就好。
🔗CyclicBarrier 的简单使用
首先,需要判断有哪些关卡,设计好通关条件。
然后,创建闯关的线程,各自吃好喝好,准备闯关。
然后,每过一关,可以有看守的线程出来引导,这也是一个任务。
之后,就可以开始了。
🔗CyclicBarrier 的API
CyclicBarrier是来做多线程同步的,首先需要确定有多少个线程参与同步。每一个线程都需要调用await()表示自己已经就绪;当所有线程都调用了await()之后,CyclicBarrier达到了第一个屏障。此时可以简单地放行,也可以设置一个任务,由最后一个就绪的线程执行,执行完才放行。
- CyclicBarrier(int n) 构造一个有n个线程参与同步的同步器,阻塞所有线程直到阻塞的线程个数大于等于n。
- CyclicBarrier(int n, Runnable action) 构造一个有n个线程参与同步的同步器,阻塞所有线程直到阻塞的线程个数大于等于n。接触阻塞之前,由最后一个达到的新城执行action。
- int await() throws InterruptedException, BrokenBarrierException 阻塞调用线程,直到所有n个线程都调用了本方法。最后一个调用本方法的线程,需要去执行设置的阻塞任务,如果设置了的话。
- int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException 同上,加上了超时限制。返回值表示还有多少个线程未就绪,0表示调用线程是最后一个线程。
- int getParties() 返回构造器传入的n值。
- boolean isBroken() 是否被损坏,损坏原因可能是线程被中断或者超时,或者阻塞action发生异常。
- void reset() 重置到初始化状态。
- int getNumberWaiting() 返回当前已经等待的线程数。
CyclicBarrier内部是通过一个ReentrantLock实例来进行同步的,用该实例的一个Condition实例来控制是否达到放行状态。
/** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); /** Condition to wait on until tripped */ private final Condition trip = lock.newCondition();
🔗示例代码
package me.wbprime.showcase.concurrent; import com.google.common.collect.ImmutableList; import org.joda.time.LocalDate; import java.util.List; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Class: CyclicBarrierCase * Date: 2016/03/30 13:35 */ public final class CyclicBarrierCase { private static class Interviewee implements Runnable { private CyclicBarrier barrier; private List<String> jobs; public Interviewee(final List<String> dt, final CyclicBarrier b) { barrier = b; jobs = dt; } public void run() { try { final String myName = Thread.currentThread().getName(); final Random rnd = new Random(System.currentTimeMillis()); for (final String eachJob: jobs) { barrier.await(); System.out.println(myName + ": Start processing job: " + eachJob); final int sleepTime = rnd.nextInt(1000) + 1; Thread.sleep(sleepTime); System.out.println(myName + ": Finish processing job: " + eachJob); } } catch (InterruptedException e) { // do nothing } catch (BrokenBarrierException e) { // do nothing } } } private static class HR implements Runnable { public HR() { } public void run() { System.out.println("Hello everyone, go on to next challenge"); } } public static void main(final String[] args) { final int memberCount = 3; final List<String> workflow = ImmutableList.of( "Self introduction", "Coding exam", "Tech interview", "Leader interview", "HR interview", "Offer" ); final CyclicBarrier barrier = new CyclicBarrier(memberCount, new HR()); final ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < memberCount; i ++) { executor.execute(new Interviewee(workflow, barrier)); } executor.shutdown(); } }
🔗示例代码说明
-
某公司公开招聘,一共有3个人过来面试,HR进行安排。
-
Interviewee类表征前来面试的人,手上有一份日程表(jobs),每一个任务都需要HR领着所有人一起开始。
-
HR类表征协调的人力资源,每次准备下一关时,可以给大家解释疑问,加油打气。
-
main函数里面,创建日程表,创建CyclicBarrier实例,创建HR和应试者,然后大家开始面试,从自我介绍开始吧。