Java多线程编程-栅栏CyclicBarrier实例

news2025/1/23 12:09:56

前言

本文是基于《Java多线程编程实战指南-核心篇》第五章个人理解,源码是摘抄作者的源码,源码会加上自己的理解。读书笔记目前笔者正在更新如下, 《Java多线程编程实战指南-核心篇》,《How Tomcat Works》,再到《spring 源码》解读。

栅栏CyclicBarrier


有时候多个线程可能需要相互等待对方执行到代码中某个地方(集合点),这这些线程才能继续执行。这种等待类似于大家相约去爬山的情形:大家实现约定好时间到集合点,先到的人必须在集合点等待其他未到的人,只有所有参与人员到齐之后大家才能够触发去登山。Java.util.concurrent.CyclicBarrier,该类就可以用来实现这种等待。

使用CyclicBarrier实现等待的线程被成为参与方,参与方只需要执行CyclicBarrier.await()就可以实现等待。尽管从应用代码的角度来看,参与方是并发执行CyclicBarrier.await()的,但是CyclicBarrier内部维护了一个显示锁,总是能区分出最后一个执行CyclicBarrier.await()的线程。除了最后一个线程外,任何参与方都被暂停,处于WAITING状态。最后一个线程执行await时候,会唤醒其他参与方,而最后一个线程自身不会暂停。

与CountDownLatch不同(有关CountDownLatch可以参考之前博客),在所有参与方唤醒时候,任何线程再次执行await又会暂停,直到这些线程最后一个线程执行了await

实例

实例模拟了士兵参与打靶训练,所有参与训练士兵被分为若干组Rank,每组被称为一排,一排士兵个数等于靶子的个数,每次只能够有一排士兵进行射击。一排士兵必须同时射击,射击完毕的士兵必须等待同排其他士兵射击完毕后才能整排撤离射击点。然后一排一排的轮流射击。

代码基本讲解

1.主函数ShootPractice实例化,输入参数为N(每排士兵个数=靶子个数),linecount(排数),lasting(最大持续时间),并且根据士兵数量(N*linecount)实例化了所有士兵存于rank中.

2.实例化了两个CycliBarrier,shiftBarrier和startBarrier,startBarrier保证同一时间士兵能在同一时间射击,可以理解为上图所示,士兵是否就绪,如图士兵一和四是站在射击地点了,处于就绪状态,需要等待士兵二和三。

而shiftBarrier,是确保所有士兵射击后同时撤离射击位置,这是由于有的士兵有的已经完成射击,有的还没有,如图士兵一和二已经射击结束,三和四还在射击,所以士兵一和二需要等待三和四。

3.所以在线程run函数的,在开火fire函数前后有两个等待,一个是等待所有人就绪,一个是等待所有人射击完成。

代码

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class ShootPractice {
  // 参与打靶训练的全部士兵
  final Soldier[][] rank;
  // 靶的个数,即每排中士兵的个数
  final int N;
  // 打靶持续时间(单位:秒)
  final int lasting;
  // 标识是否继续打靶
  volatile boolean done = false;
  // 用来指示进行下一轮打靶的是哪一排的士兵
  volatile int nextLine = 0;
  final CyclicBarrier shiftBarrier;
  final CyclicBarrier startBarrier;

  public ShootPractice(int N, final int lineCount, int lasting) {
    this.N = N;
    this.lasting = lasting;
    this.rank = new Soldier[lineCount][N];
    for (int i = 0; i < lineCount; i++) {
      for (int j = 0; j < N; j++) {
        rank[i][j] = new Soldier(i * N + j);
      }
    }
    shiftBarrier = new CyclicBarrier(N, new Runnable() {
      @Override
      public void run() {
        // 更新下一轮打靶的排
        nextLine = (nextLine + 1) % lineCount;// 语句①
        Debug.info("Next turn is :%d", nextLine);
      }
    });
    // 语句②
    startBarrier = new CyclicBarrier(N);
  }

  public static void main(String[] args) throws InterruptedException {
    ShootPractice sp = new ShootPractice(4, 5, 24);
    sp.start();
  }

  public void start() throws InterruptedException {
    // 创建并启动工作者线程
    Thread[] threads = new Thread[N];
    for (int i = 0; i < N; ++i) {
      threads[i] = new Shooting(i);
      threads[i].start();
    }
    // 指定时间后停止打靶
    Thread.sleep(lasting * 1000);
    stop();
    for (Thread t : threads) {
      t.join();
    }
    Debug.info("Practice finished.");
  }

  public void stop() {
    done = true;
  }

  class Shooting extends Thread {
    final int index;

    public Shooting(int index) {
      this.index = index;
    }

    @Override
    public void run() {
      Soldier soldier;
      try {
        while (!done) {
          soldier = rank[nextLine][index];
          // 一排中的士兵必须同时开始射击
          startBarrier.await();// 语句③
          // 该士兵开始射击
          soldier.fire();
          // 一排中的士兵必须等待该排中的所有其他士兵射击完毕才能够离开射击点
          shiftBarrier.await();// 语句④
        }
      } catch (InterruptedException e) {
        // 什么也不做
      } catch (BrokenBarrierException e) {
        e.printStackTrace();
      }

    }// run方法结束
  }// 类Shooting定义结束

  // 参与打靶训练的士兵
  static class Soldier {
    private final int seqNo;

    public Soldier(int seqNo) {
      this.seqNo = seqNo;
    }

    public void fire() {
      Debug.info(this + " start firing...");
      Tools.randomPause(5000);
      System.out.println(this + " fired.");
    }

    @Override
    public String toString() {
      return "Soldier-" + seqNo;
    }

  }// 类Soldier定义结束
}

debug和tool可以参考之前博客

参考文献

《Java多线程编程实战-核心篇》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1045971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT中计算日期差,并进行加减

1、界面上拖动两个QDateTimeEdit控件&#xff0c;同时设置为开始时间与结束时间&#xff0c;然后再来拖动个pushButton&#xff0c;命名为查询功能&#xff0c;然后槽函数中&#xff0c;实现如下&#xff1a; void Database::on_pushButton_4_clicked() {QDateTime time1 u…

ARM/X86工控机在轨道交通车站管理系统的应用(1)

车站管理系统 车站管理系统是一种集成平台&#xff0c;包括综合监控系统(ISCS)、火灾报警系统(FAS)、以及楼宇自动化系统(BAS)等。由于其复杂性&#xff0c;车站管理系统要求硬件必须稳定且可扩展&#xff0c;以确保24/7全天侯正常运作。信迈工业服务器级系统素以坚固耐用和广…

x86汇编基础

目录 CPU架构与指令集 x86 / x64 CPU操作模式 寄存器 数据类型 数据传送与访问 算数逻辑与运算逻辑 跳转指令和循环指令 栈与函数调用 这一部分更详细的内容可以参考我的专栏&#xff1a;C与汇编 CPU架构与指令集 CPU即中央处理单元(Central Processing Unit )&#…

虚拟机联网

桥接 桥接模式就是虚拟机与你的电脑平起平做&#xff0c;都有同样的IP&#xff0c;且与你的电脑在同一网段下&#xff0c;就能够上网。 电脑的IP的地址 虚拟机的ip地址 设置vm1的ip地址与网关与电脑相同 如果出现ssh连接虚拟机不成功的问题&#xff0c;无其他问题时&#xff0…

2023-09-27 LeetCode每日一题(餐厅过滤器)

2023-09-27每日一题 一、题目编号 1333. 餐厅过滤器二、题目链接 点击跳转到题目位置 三、题目描述 给你一个餐馆信息数组 restaurants&#xff0c;其中 restaurants[i] [idi, ratingi, veganFriendlyi, pricei, distancei]。你必须使用以下三个过滤器来过滤这些餐馆信息…

Python经典练习题(四)

文章目录 &#x1f340;第一题&#x1f340;第二题&#x1f340;第三题 &#x1f340;第一题 题目&#xff1a;打印出如下图案&#xff08;菱形&#xff09;: 我们首先分析一下&#xff0c;本题实现的步骤主要有两个&#xff0c;分别是前四行和后三行 前四行&#xff1a;第一…

数据结构--二叉树(2)

文章目录 二叉树的存储结构二叉树的链式结构二叉树的遍历结点个数寻找二叉树的某个结点二叉树的层遍历判断是否为完全二叉树 上一节 二叉树的堆链接入口 二叉树的存储结构 对于二叉树的存储&#xff0c;有两种存储方式&#xff1a;一种是顺序存储&#xff0c;另一种是链式存储…

linux 清除卸载jenkins

1、停服务进程 查看jenkins服务是否在运行&#xff0c;如果在运行&#xff0c;停掉 查看服务 ps -ef|grep jenkins 停掉进程 kill -9 XXX2、查找安装目录 find / -name "jenkins*"3、删掉相关目录 删掉相关安装目录 rm -rf /root/.jenkins/# 删掉war包 rm -rf /…

服务断路器_Resilience4j异常比例熔断降级

给coud-consumer-feign-order80添加resilience4j依赖 修改yml文件 resilience4j.circuitbreaker:configs:default:# 熔断器打开的失败阈值failureRateThreshold: 30# 默认滑动窗口大小&#xff0c;circuitbreaker使用基于计数和时间范围欢动窗口聚合统计失败率slidingWindowS…

如何写公众号推文?公众号文章写作步骤分享

一篇优质的公众号文章&#xff0c;不仅能提升品牌知名度&#xff0c;增强用户粘性&#xff0c;还能引导潜在客户&#xff0c;实现商业价值。那么&#xff0c;如何才能写出一篇引人入胜的公众号文章呢&#xff1f;本文伯乐网络传媒将为您详细解析写公众号文章的步骤&#xff0c;…

模块接口测试

单元测试是代码正确性验证的最重要的工具&#xff0c;也是系统测试当中最重要的环节。也是唯一需要编写代码才能进行测试的一种测试方法。在标准的开发过程中&#xff0c;单元测试的代码与实际程序的代码具有同等的重要性。每一个单元测试&#xff0c;都是用来定向测试其所对应…

【笔记】Splay

【笔记】Splay 目录 简介右旋左旋 核心思想操作a. Splayb. 插入c. 删除 信息的维护例题AcWing 2437. SplayP3369 【模板】普通平衡树 简介 Splay 是一种平衡树&#xff0c;并且是一棵二叉搜索树&#xff08;BST&#xff09;。 它满足对于任意节点&#xff0c;都有左子树上任意…

fdbus之CBaseMessage

总体介绍 这个类是一个很重要的的类&#xff0c;fdbus中传递对象就是这个类的实例&#xff0c;该类中包含了很多重要的信息。可以这样理解&#xff0c;再fdbus的通信中&#xff0c;这个类的地位至关重要。他们的通信的内容就是该类定义的一些信息。 虽然CFdbBaseObject定义了…

如何进一步全面提高项目估算精准度?

项目估算非常重要&#xff0c;这直接关系着项目的成本和收入&#xff0c;如果估算不准确&#xff0c;将为项目带来较大风险。一般软件规模可以用多种方式进行估算&#xff0c;但是用功能点估算方式更准确&#xff0c;而自动估算让估算更快速&#xff0c;我们以CoCode开发的估算…

初识网络编程

一、概述 地球村&#xff1a;亦称世界村&#xff0c;是通过电子媒介将世界紧密联系起来的形象表达&#xff0c;是信息网络时代的集中体现 TCP和UDP&#xff1a; TCP&#xff1a;打电话 -->连接 -->接了 -->通话 UDP&#xff1a;发送完即可 -->接收 计算机网络&a…

QQ表情包存储位置解析

一些常见的设备和系统的QQ表情包存储位置&#xff1a; Windows系统&#xff1a; 路径&#xff1a;C:\Users[用户名]\Documents\Tencent Files[QQ号码]\Image\Image\CustomFace 在这个文件夹中&#xff0c;您可以找到所有自定义的QQ表情包。 Android系统&#xff1a; 路径&am…

程序开发常用在线工具汇总

菜鸟工具# https://c.runoob.com/ 编码# ASCII码# https://www.habaijian.com/ 在线转换# https://www.107000.com/T-Ascii/http://www.ab126.com/goju/1711.html Base64# 在线转换# https://www.qqxiuzi.cn/bianma/base64.htmhttp://www.mxcz.net/tools/Unicode.aspx …

软件架构的演化和维护

软件架构的演化和维护 定义 定义 顶不住了&#xff0c;刷题去了&#xff0c;不搞这个了&#xff0c;想吐。。。

STM32Cube 开发之读写内部Flash--电源项目ADC采样校准系数存储-实现掉电读取数据--STM32或者GD32F处理器

STM32Cube 开发之读写内部Flash–电源项目ADC采样校准系数存储-实现掉电读取数据 一、需求介绍 1.1 在进行电源项目开发中&#xff0c;输入与输出的电压电流经过硬件电路分压或者差分变换后&#xff0c;将低压的电压信号给到单片机如STM32F1系列单片机的ADC采样端口&#xff…

网速Full Power!这款4G网关信号达360度无死角

数字化转型浪潮下,如何实现可靠的无线互联成为制造企业面临的新课题。广州数智自动化最近通过部署星创SG500 4G网关,成功实现了某工业园区全域无线覆盖和多系统安全访问。 SG500支持全球主流的4G网络频段,可灵活搭配通信运营商,提供高达150Mbps的无线传输速率。它采用强大的四核…