CyclicBarrier 原理(秒懂) 您所在的位置:网站首页 unlock读音 CyclicBarrier 原理(秒懂)

CyclicBarrier 原理(秒懂)

2023-12-10 13:17| 来源: 网络整理| 查看: 265

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 经典图书:《尼恩Java面试宝典 最新版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领 免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取

JUC 高并发工具类(3文章)与高并发容器类(N文章) : 1 CyclicBarrier 使用&核心原理 图解 2 countDownLatch 使用&核心原理 图解 3 Semaphore 使用&核心原理 图解 4 跳表 核心原理 图解 5 ConcurrentSkipListMap - 秒懂 6 ConcurrentSkipListSet - 秒懂 1 CyclicBarrier 是什么?

从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。

它的作用就是会让所有线程都等待完成后才会继续下一步行动。

现实生活中我们经常会遇到这样的情景,在进行某个活动前需要等待人全部都齐了才开始。例如吃饭时要等全家人都上座了才动筷子,旅游时要等全部人都到齐了才出发,比赛时要等运动员都上场后才开始。

在JUC包中为我们提供了一个同步工具类能够很好的模拟这类场景,它就是CyclicBarrier类。利用CyclicBarrier类可以实现一组线程相互等待,当所有线程都到达某个屏障点后再进行后续的操作。下图演示了这一过程。

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

CyclicBarrier字面意思是“可重复使用的栅栏”,CyclicBarrier 相比 CountDownLatch 来说,要简单很多,其源码没有什么高深的地方,它是 ReentrantLock 和 Condition 的组合使用。

看如下示意图,CyclicBarrier 和 CountDownLatch 是不是很像,只是 CyclicBarrier 可以有不止一个栅栏,因为它的栅栏(Barrier)可以重复使用(Cyclic)。

cyclicbarrier-2

2 怎么使用 CyclicBarrier 2.1 构造方法 public CyclicBarrier(int parties) public CyclicBarrier(int parties, Runnable barrierAction)

解析:

parties 是参与线程的个数 第二个构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务

2.2 重要方法 public int await() throws InterruptedException, BrokenBarrierException public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

解析:

线程调用 await() 表示自己已经到达栅栏 BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

3 使用案例

2.3.1 需求 一个线程组的线程需要等待所有线程完成任务后再继续执行下一次任务

2.3.2 代码实现

public class CyclicBarrierDemo { static class TaskThread extends Thread { CyclicBarrier barrier; public TaskThread(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { try { Thread.sleep(1000); System.out.println(getName() + " 到达栅栏 A"); barrier.await(); System.out.println(getName() + " 冲破栅栏 A"); Thread.sleep(2000); System.out.println(getName() + " 到达栅栏 B"); barrier.await(); System.out.println(getName() + " 冲破栅栏 B"); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { int threadNum = 5; CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " 完成最后任务"); } }); for(int i = 0; i < threadNum; i++) { new TaskThread(barrier).start(); } } }

打印结果:

Thread-1 到达栅栏 A Thread-3 到达栅栏 A Thread-0 到达栅栏 A Thread-4 到达栅栏 A Thread-2 到达栅栏 A Thread-2 完成最后任务 Thread-2 冲破栅栏 A Thread-1 冲破栅栏 A Thread-3 冲破栅栏 A Thread-4 冲破栅栏 A Thread-0 冲破栅栏 A Thread-4 到达栅栏 B Thread-0 到达栅栏 B Thread-3 到达栅栏 B Thread-2 到达栅栏 B Thread-1 到达栅栏 B Thread-1 完成最后任务 Thread-1 冲破栅栏 B Thread-0 冲破栅栏 B Thread-4 冲破栅栏 B Thread-2 冲破栅栏 B Thread-3 冲破栅栏 B

从打印结果可以看出,所有线程会等待全部线程到达栅栏之后才会继续执行,并且最后到达的线程会完成 Runnable 的任务。 在这里插入图片描述

CyclicBarrier 使用场景

可以用s于多线程计算数据,最后合并计算结果的场景。

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

4 CyclicBarrier 原理

而 CyclicBarrier 基于 Condition 来实现的。因为 CyclicBarrier 的源码相对来说简单许多,读者只要熟悉了前面关于 Condition 的分析,那么这里的源码是毫无压力的,就是几个特殊概念罢了。

在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待的原理,下面我们先看看CyclicBarrier有哪些成员变量。

成员变量 //同步操作锁 private final ReentrantLock lock = new ReentrantLock(); //线程拦截器 private final Condition trip = lock.newCondition(); //每次拦截的线程数 private final int parties; //换代前执行的任务 private final Runnable barrierCommand; //表示栅栏的当前代 private Generation generation = new Generation(); //计数器 private int count; //静态内部类Generation private static class Generation { boolean broken = false; }

上面贴出了CyclicBarrier所有的成员变量,可以看到CyclicBarrier内部是通过条件队列trip来对线程进行阻塞的,并且其内部维护了两个int型的变量parties和count,parties表示每次拦截的线程数,该值在构造时进行赋值。count是内部计数器,它的初始值和parties相同,以后随着每次await方法的调用而减1,直到减为0就将所有线程唤醒。CyclicBarrier有一个静态内部类Generation,该类的对象代表栅栏的当前代,就像玩游戏时代表的本局游戏,利用它可以实现循环等待。barrierCommand表示换代前执行的任务,当count减为0时表示本局游戏结束,需要转到下一局。在转到下一局游戏之前会将所有阻塞的线程唤醒,在唤醒所有线程之前你可以通过指定barrierCommand来执行自己的任务。我用一图来描绘下 CyclicBarrier 里面的一些概念:

构造器

接下来我们看看它的构造器。

//构造器1 public CyclicBarrier(int parties, Runnable barrierAction) { if (parties 0L) { nanos = trip.awaitNanos(nanos); } } catch (InterruptedException ie) { //若当前线程在等待期间被中断则打翻栅栏唤醒其他线程 if (g == generation && ! g.broken) { breakBarrier(); throw ie; } else { //若在捕获中断异常前已经完成在栅栏上的等待, 则直接调用中断操作 Thread.currentThread().interrupt(); } } //如果线程因为打翻栅栏操作而被唤醒则抛出异常 if (g.broken) { throw new BrokenBarrierException(); } //如果线程因为换代操作而被唤醒则返回计数器的值 if (g != generation) { return index; } //如果线程因为时间到了而被唤醒则打翻栅栏并抛出异常 if (timed && nanos


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有