java高并发系列 - 第17天:JUC中的循环栅栏CyclicBarrier常见的6种使用场景及代码示例

6个示例介绍CyclicBarrier的使用

对比CyclicBarrier和CountDownLatch

CyclicBarrier简介

CyclicBarrier通常称为循环屏障。它和CountDownLatch很相似,都可以使线程先等待然后再执行。不过CountDownLatch是使一批线程等待另一批线程执行完后再执行;而CyclicBarrier只是使等待的线程达到一定数目后再让它们继续执行。故而CyclicBarrier内部也有一个计数器,计数器的初始值在创建对象时通过构造参数指定,如下所示:

public CyclicBarrier(int parties) { this(parties, null); }

每调用一次await()方法都将使阻塞的线程数+1,只有阻塞的线程数达到设定值时屏障才会打开,允许阻塞的所有线程继续执行。除此之外,CyclicBarrier还有几点需要注意的地方:

CyclicBarrier的计数器可以重置而CountDownLatch不行,这意味着CyclicBarrier实例可以被重复使用而CountDownLatch只能被使用一次。而这也是循环屏障循环二字的语义所在。

CyclicBarrier允许用户自定义barrierAction操作,这是个可选操作,可以在创建CyclicBarrier对象时指定

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

一旦用户在创建CyclicBarrier对象时设置了barrierAction参数,则在阻塞线程数达到设定值屏障打开前,会调用barrierAction的run()方法完成用户自定义的操作。

示例1:简单使用CyclicBarrier

公司组织旅游,大家都有经历过,10个人,中午到饭点了,需要等到10个人都到了才能开饭,先到的人坐那等着,代码如下:

package com.itsoku.chat15; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; /** * 微信公众号:javacode2018,获取年薪50万java课程 */ public class Demo1 { public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10); public static class T extends Thread { int sleep; public T(String name, int sleep) { super(name); this.sleep = sleep; } @Override public void run() { try { //模拟休眠 TimeUnit.SECONDS.sleep(sleep); long starTime = System.currentTimeMillis(); //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续 cyclicBarrier.await(); long endTime = System.currentTimeMillis(); System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 10; i++) { new T("员工" + i, i).start(); } } }

输出:

员工1,sleep:1 等待了9000(ms),开始吃饭了! 员工9,sleep:9 等待了1000(ms),开始吃饭了! 员工8,sleep:8 等待了2001(ms),开始吃饭了! 员工7,sleep:7 等待了3001(ms),开始吃饭了! 员工6,sleep:6 等待了4001(ms),开始吃饭了! 员工4,sleep:4 等待了6000(ms),开始吃饭了! 员工5,sleep:5 等待了5000(ms),开始吃饭了! 员工10,sleep:10 等待了0(ms),开始吃饭了! 员工2,sleep:2 等待了7999(ms),开始吃饭了! 员工3,sleep:3 等待了7000(ms),开始吃饭了!

代码中模拟了10个员工上桌吃饭的场景,等待所有员工都到齐了才能开发,可以看到第10个员工最慢,前面的都在等待第10个员工,员工1等待了9秒,上面代码中调用cyclicBarrier.await();会让当前线程等待。当10个员工都调用了cyclicBarrier.await();之后,所有处于等待中的员工都会被唤醒,然后继续运行。

示例2:重复使用CyclicBarrier

对示例1进行改造一下,吃饭完毕之后,所有人都去车上,待所有人都到车上之后,驱车去下一景点玩。

package com.itsoku.chat15; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; /** * 微信公众号:javacode2018,获取年薪50万java课程 */ public class Demo2 { public static CyclicBarrier cyclicBarrier = new CyclicBarrier(10); public static class T extends Thread { int sleep; public T(String name, int sleep) { super(name); this.sleep = sleep; } //等待吃饭 void eat() { try { //模拟休眠 TimeUnit.SECONDS.sleep(sleep); long starTime = System.currentTimeMillis(); //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续 cyclicBarrier.await(); long endTime = System.currentTimeMillis(); System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),开始吃饭了!"); //休眠sleep时间,模拟当前员工吃饭耗时 TimeUnit.SECONDS.sleep(sleep); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } //等待所有人到齐之后,开车去下一站 void drive() { try { long starTime = System.currentTimeMillis(); //调用await()的时候,当前线程将会被阻塞,需要等待其他员工都到达await了才能继续 cyclicBarrier.await(); long endTime = System.currentTimeMillis(); System.out.println(this.getName() + ",sleep:" + this.sleep + " 等待了" + (endTime - starTime) + "(ms),去下一景点的路上!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } @Override public void run() { //等待所有人到齐之后吃饭,先到的人坐那等着,什么事情不要干 this.eat(); //等待所有人到齐之后开车去下一景点,先到的人坐那等着,什么事情不要干 this.drive(); } } public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 10; i++) { new T("员工" + i, i).start(); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzwwd.html