重庆购务网站建设,济南行知网站建设,松江网站建设多少钱,qq炫舞开发公司作者 | 王磊来源 | Java中文社群#xff08;ID#xff1a;javacn666#xff09;转载请联系授权#xff08;微信ID#xff1a;GG_Stone#xff09;上一篇咱讲了 CountDownLatch 可以解决多个线程同步的问题#xff0c;相比于 join 来说它的应用范围更广#xff0c;不仅可… 作者 | 王磊来源 | Java中文社群IDjavacn666转载请联系授权微信IDGG_Stone上一篇咱讲了 CountDownLatch 可以解决多个线程同步的问题相比于 join 来说它的应用范围更广不仅可以应用在线程上还可以应用在线程池上。然而 CountDownLatch 却是一次性的计数器以王者农药来说咱们不可能一场团战就决定比赛的输赢所以在某些场景下咱们是需要重复使用某个等待功能的这就是我们今天要介绍的另一个主角——CyclicBarrier。CyclicBarrierCyclicBarrier 翻译为中文是循环Cyclic栅栏Barrier的意思它的大概含义是实现一个可循环利用的屏障。CyclicBarrier 作用是让一组线程相互等待当达到一个共同点时所有之前等待的线程再继续执行且 CyclicBarrier 功能可重复使用。举个栗子比如磊哥要坐班车回老家因为中途不允许上、下乘客所以营运的公司为了收益最大化就会等人满之后再发车。像这种等人坐满就发一班车的场景就是 CyclicBarrier 所擅长的因为它可以重复使用不像 CountDownLatch 那样只能用一次。CyclicBarrier VS CountDownLatchCountDownLatch一个或者多个线程等待另外 N 个线程完成某个事情之后才能执行。CountDownLatch 就像玩王者农药开局的加载一样所有人要等待其他人都加载 100% 之后才能开始游戏。CyclicBrrierN 个线程相互等待直到有足够数量的线程都到达屏障点之后之前等待的线程就可以继续执行了。CyclicBrrier 就像老司机开车一样如果车上还有空余的座位那么所有人都得等着直到座位被坐满之后老司机才会发车。CyclicBarrier使用import java.util.Date;
import java.util.Random;
import java.util.concurrent.*;public class CyclicBarrierExample {public static void main(String[] args) {// 创建 CyclicBarrierfinal CyclicBarrier cyclicBarrier new CyclicBarrier(2, new Runnable() {Overridepublic void run() {System.out.println(人满了准备发车 new Date());}});// 线程调用的任务Runnable runnable new Runnable() {Overridepublic void run() {// 生成随机数 1-3int randomNumber new Random().nextInt(3) 1;// 进入任务System.out.println(String.format(我是%s 再走%d 秒就到车站了现在时间%s,Thread.currentThread().getName(), randomNumber, new Date()));try {// 模拟执行TimeUnit.SECONDS.sleep(randomNumber);// 调用 CyclicBarriercyclicBarrier.await();// 任务执行System.out.println(String.format(线程%s 上车时间%s,Thread.currentThread().getName(), new Date()));} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}};// 创建线程池ExecutorService threadPool Executors.newFixedThreadPool(10);// 执行任务 1threadPool.submit(runnable);// 执行任务 2threadPool.submit(runnable);// 执行任务 3threadPool.submit(runnable);// 执行任务 4threadPool.submit(runnable);// 等待所有任务执行完终止线程池threadPool.shutdown();}
}
以上代码执行结果如下从上述结果可以看出当 CyclicBarrier 的计数器设置为 2 时线程 2 和 线程 3 都到屏障点之后老司机才会发第一波车再 2s 之后线程 1 和线程 4 也同时进入了屏障点这时候老司机又可以再发一波车了。实现原理我们先来看下 CyclicBarrier 的类图由上图可知 CyclicBarrier 是基于独占锁 ReentrantLock 实现的其底层也是基于 AQS 的。在 CyclicBarrier 类的内部有一个计数器 count当 count 不为 0 时每个线程在到达屏障点会先调用 await 方法将自己阻塞此时计数器会减 1直到计数器减为 0 的时候所有因调用 await 方法而被阻塞的线程就会被唤醒继续执行。当 count 计数器变成 0 之后就会进入下一轮阻塞此时 partiesparties 是在 new CyclicBarrier(parties) 时设置的值会将它的值赋值给 count 从而实现复用。常用方法CyclicBarrier(parties)初始化相互等待的线程数量的构造方法。CyclicBarrier(parties,Runnable barrierAction)初始化相互等待的线程数量以及屏障线程的构造方法当 CyclicBarrier 的计数器变为 0 时会执行 barrierAction 构造方法。getParties()获取 CyclicBarrier 打开屏障的线程数量也称为方数。getNumberWaiting()获取正在CyclicBarrier上等待的线程数量。await()在 CyclicBarrier 上进行阻塞等待直到发生以下情形之一在 CyclicBarrier 上等待的线程数量达到 parties则所有线程被释放继续执行当前线程被中断则抛出 InterruptedException 异常并停止等待继续执行其他等待的线程被中断则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行其他等待的线程超时则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行其他线程调用 CyclicBarrier.reset() 方法则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行。await(timeout,TimeUnit)在CyclicBarrier上进行限时的阻塞等待直到发生以下情形之一在 CyclicBarrier 上等待的线程数量达到 parties则所有线程被释放继续执行当前线程被中断则抛出 InterruptedException 异常并停止等待继续执行当前线程等待超时则抛出 TimeoutException 异常并停止等待继续执行其他等待的线程被中断则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行其他等待的线程超时则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行其他线程调用 CyclicBarrier.reset() 方法则当前线程抛出 BrokenBarrierException 异常并停止等待继续执行。isBroken()获取是否破损标志位 broken 的值此值有以下几种情况CyclicBarrier 初始化时brokenfalse表示屏障未破损如果正在等待的线程被中断则 brokentrue表示屏障破损如果正在等待的线程超时则 brokentrue表示屏障破损如果有线程调用 CyclicBarrier.reset() 方法则 brokenfalse表示屏障回到未破损状态。reset()使得CyclicBarrier回归初始状态直观来看它做了两件事如果有正在等待的线程则会抛出 BrokenBarrierException 异常且这些线程停止等待继续执行。将是否破损标志位 broken 置为 false。总结CyclicBrrier 是通过独占锁 ReentrantLock 实现计数器的原子性更新的CyclicBrrier 最常用的是 await() 方法使用此方法会将计数器 -1并判断当前的计数器是否为 0如果不为 0 就会阻塞等待并计时器为 0 之后才能继续执行剩余任务。CyclicBrrier 相比于 CountDownLatch 来说它的优势在于可以重复使用。参考 鸣谢blog.csdn.net/qq_39241239/article/details/87030142blog.csdn.net/zzg1229059735/article/details/61191679www.cnblogs.com/yaochunhui/p/13494689.html