CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到到达某个公共屏障点。
与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障(Barrier)。
CyclicBarrier API:
1、第一个默认构造方法,参数表示拦截的线程数量。
2、第二个构造方法:由于线程之前的调度是由CPU决定的,所以默认的构造方法无法设置线程执行优先级,CyclicBarrier提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction)
,用于在线程到达同步点时,优先执行线程barrierAction,这样可以更加方便的处理一些负责的业务场景。
3、await实现
创建CyclicBarrier后,每个线程调用await方法告诉CyclicBarrier自己已经到达同步点,然后当前线程被阻塞
比较CountDownLatch和CyclicBarrier:
1)CountDownLatch:一个线程(或者多个),等待另外N个线程完成某个事情之后才能执行;CyclicBarrier:N个线程相互等待, 任何一个线程完成之前,所有的线程都必须等待。
2)CountDownLatch:一次性的;CyclicBarrier:可以重复使用。
3)CountDownLatch基于AQS;CyclicBarrier基于锁和Condition。本质上都是依赖于volatile和CAS实现的。
对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。
下面举例说明CyclicBarrier的使用场景。
比如:篮球三人一组才能开站,等到三个人,他们一组就去打篮球了。
public class Test {
private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(3,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
//CountDownLatch countdl=new CountDownLatch(3);
CyclicBarrier countdl =new CyclicBarrier(3,new Runnable() {
public void run()
{
System.out.println("开始打篮球了");
}});
public static void main(String[] args) {
final Test test=new Test();
Thread zhansan=new Thread(new Player(test.countdl,"zhansan"));
Thread zhaner=new Thread(new Player(test.countdl,"zhaner"));
Thread zhanyi=new Thread(new Player(test.countdl,"zhanyi"));
Thread zhansan2=new Thread(new Player(test.countdl,"zhansan2"));
Thread zhaner2=new Thread(new Player(test.countdl,"zhaner2"));
Thread zhanyi2=new Thread(new Player(test.countdl,"zhanyi2"));
threadPool.execute(zhansan);
threadPool.execute(zhaner);
threadPool.execute(zhanyi);
threadPool.execute(zhansan2);
threadPool.execute(zhaner2);
threadPool.execute(zhanyi2);
}
}
class Player implements Runnable{
private CyclicBarrier ctd;
private String name;
public Player(CyclicBarrier ctd,String name){
this.ctd=ctd;
this.name=name;
}
public void run() {
try {
System.out.println(this.name+",开始出发了,"+new Date());
Thread.sleep(Math.round(1000));
ctd.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下:
zhansan,开始出发了,Thu Jul 19 21:10:44 CST 2018
zhanyi,开始出发了,Thu Jul 19 21:10:44 CST 2018
zhaner,开始出发了,Thu Jul 19 21:10:44 CST 2018
开始打篮球了
zhansan2,开始出发了,Thu Jul 19 21:10:45 CST 2018
zhanyi2,开始出发了,Thu Jul 19 21:10:45 CST 2018
zhaner2,开始出发了,Thu Jul 19 21:10:45 CST 2018
开始打篮球了