java-CyclicBarrier、CountDownLatch、Semaphore 的用法以及 volatile 关键字的作用

news2025/1/20 6:01:17

CyclicBarrier、CountDownLatch、Semaphore 的用法

1. CountDownLatch(线程计数器 )

  • CountDownLatch 类位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。比如有一个任务 A,它要等待其他 4 个任务执行完毕之后才能执行,此时就可以利用 CountDownLatch来实现这种功能了。
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){public void run() {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
};}.start();
new Thread(){ public void run() {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
};}.start();
System.out.println("等待 2 个子线程执行完毕...");
latch.await();
System.out.println("2 个子线程已经执行完毕");
System.out.println("继续执行主线程");
}

2. CyclicBarrier(回环栅栏-等待至 barrier 状态再全部同时执行)

  • 字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环
  • 是因为当所有等待线程都被释放以后,CyclicBarrier 可以被重用。我们暂且把这个状态就叫做barrier,当调用 await()方法之后,线程就处于 barrier 了。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 个重载版本:
  1. public int await():用来挂起当前线程,直至所有线程都到达 barrier 状态再同时执行后续任务;
  2. public int await(long timeout, TimeUnit unit):让这些线程等待至一定的时间,如果还有线程没有到达 barrier 状态就直接让到达 barrier 的线程执行后续任务。
具体使用如下,另外 CyclicBarrier 是可以重用的。
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(5000); //以睡眠来模拟线程需要预定写入数据操作
System.out.println(" 线 程 "+Thread.currentThread().getName()+" 写 入 数 据 完
毕,等待其他线程写入完毕");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有线程写入完毕,继续处理其他任务,比如数据操作");
}
}
  1. Semaphore(信号量-控制同时访问的线程个数)
  • Semaphore 翻译成字面意思为 信号量,Semaphore 可以控制同时访问的线程个数,通过acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
Semaphore 类中比较重要的几个方法:
  1. public void acquire(): 用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许
    可。
  2. public void acquire(int permits):获取 permits 个许可
  3. public void release() { } :释放许可。注意,在释放许可之前,必须先获获得许可。
  4. public void release(int permits) { }:释放 permits 个许可
上面 4 个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法
  1. public boolean tryAcquire():尝试获取一个许可,若获取成功,则立即返回 true,若获取失败,则立即返回 false
  2. public boolean tryAcquire(long timeout, TimeUnit unit):尝试获取一个许可,若在指定的时间内获取成功,则立即返回 true,否则则立即返回 false
  3. public boolean tryAcquire(int permits):尝试获取 permits 个许可,若获取成功,则立即返回 true,若获取失败,则立即返回 false
  4. public boolean tryAcquire(int permits, long timeout, TimeUnit unit): 尝试获取 permits个许可,若在指定的时间内获取成功,则立即返回 true,否则则立即返回 false
  5. 还可以通过 availablePermits()方法得到可用的许可数目。
  • 例子:若一个工厂有 5 台机器,但是有 8 个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过 Semaphore 来实现:
int N = 8; //工人数
Semaphore semaphore = new Semaphore(5); //机器数目
for(int i=0;i<N;i++)
new Worker(i,semaphore).start();
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"占用一个机器在生产...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"释放出机器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
  • CountDownLatch 和 CyclicBarrier 都能够实现线程之间的等待,只不过它们侧重点不同;CountDownLatch 一般用于某个线程 A 等待若干个其他线程执行完任务之后,它才执行;而 CyclicBarrier 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;另外,CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的。
  • Semaphore 其实和锁有点类似,它一般用于控制对某组资源的访问权限。

volatile 关键字的作用(变量可见性、禁止重排序)

  • Java 语言提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。volatile 变量具备两种特性,volatile 变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取 volatile 类型的变量时总会返回最新写入的值。

变量可见性

  • 其一是保证该变量对所有线程可见,这里的可见性指的是当一个线程修改了变量的值,那么新的值对于其他线程是可以立即获取的。

禁止重排序

  • volatile 禁止了指令重排。比 sychronized更轻量级的同步锁

  • 在访问 volatile 变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此 volatile 变量是一种比 sychronized 关键字更轻量级的同步机制。volatile 适合这种场景:一个变量被多个线程共享,线程直接给这个变量赋值。
    在这里插入图片描述

  • 当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到 CPU 缓存中。如果计算机有多个 CPU,每个线程可能在不同的 CPU 上被处理,这意味着每个线程可以拷贝到不同的 CPUcache 中。而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache这一步。

适用场景

  • 值得说明的是对 volatile 变量的单次读/写操作可以保证原子性的,如 long 和 double 类型变量,但是并不能保证 i++这种操作的原子性,因为本质上 i++是读、写两次操作。在某些场景下可以代替 Synchronized。但是,volatile 的不能完全取代 Synchronized 的位置,只有在一些特殊的场景下,才能适用 volatile。总的来说,必须同时满足下面两个条件才能保证在并发环境的线程安全:
    1. 对变量的写操作不依赖于当前值(比如 i++),或者说是单纯的变量赋值(boolean
      flag = true)。
    2. 该变量没有包含在具有其他变量的不变式中,也就是说,不同的 volatile 变量之间,不
      能互相依赖。只有在状态真正独立于程序内其他内容时才能使用 volatile。

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

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

相关文章

powerJob报错以及解决办法集锦

1. 本地测试成功新建任务并运行成功&#xff0c;但是部署到服务器时新建任务只要 “参数”有中文就无法报错 前台报错信息&#xff1a; ERROR&#xff1a;JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException…

MySQL 小数类型介绍

文章目录 前言1. 浮点类型1.1 数值精度说明1.2 整数超出范围1.3 小数超出范围1.4 精度误差说明 2. 定点类型2.1 数值精度说明2.2 整数超出范围2.3 小数超出范围 总结 前言 对于保证精度的数字&#xff0c;MySQL 也有对应的小数类型&#xff0c;下图是 MySQL 中小数类型概览。 …

Unity 之利用 localEulerAngle与EulerAngle 控制物体旋转

文章目录 概念讲解localEulerAngle与EulerAngle的区别 概念讲解 欧拉角&#xff08;Euler Angles&#xff09;是一种常用于描述物体在三维空间中旋转的方法。它使用三个角度来表示旋转&#xff0c;分别绕物体的三个坐标轴&#xff08;通常是X、Y和Z轴&#xff09;进行旋转。这…

cvat 安装部署

官网地址&#xff1a; https://github.com/opencv/cvat/tree/masterhttps://github.com/opencv/cvat/tree/master 1.从官网上下载源码地址。 2.配置环境变量 vim /etc/profile 或者执行&#xff1a; export CVAT_HOSTyour-ip-address 3.执行命令 docker-compose up -d …

视频中的声音怎么提取出来?这样做提取出来很简单

提取视频中的声音可以有多种用途。例如&#xff0c;我们可能希望从视频中提取音乐或音效&#xff0c;以在其他项目中使用。或者&#xff0c;可能需要将视频中的对话转录为文本&#xff0c;以便更轻松地编辑和共享内容。无论目的是什么&#xff0c;提取视频中的声音都可以帮助我…

Dwg如何转dxf格式?分享三种转换小技巧

DWG格式是CAD的原生格式&#xff0c;但是DXF文件格式是CAD支持的通用格式。将DWG文件转换为DXF文件可以使其他软件能够读取和编辑这些文件&#xff0c;因为大多数CAD软件都支持DXF格式。此外&#xff0c;DXF文件比DWG文件更小&#xff0c;在传输和存储时更方便。因此&#xff0…

“摧毁我们的文明”?推特博主马斯克批评TikTok:我决定不再使用

根据8月25日的消息&#xff0c;推特博主DogeDesigner发表了关于TikTok的批评言论&#xff0c;声称这个应用正在“摧毁我们的文明”。 他在配图中展示了两种形象&#xff0c;左边的形象代表着各方面的专家知识&#xff0c;如天文学、数学、物理学、哲学和生活等&#xff0c;但似…

从LeakCanary看内存快照生成

前面我们已经完成了生命周期监控并且可以通过ReferenceQueue和WeakHashMap的比较确定哪些对象发生泄漏了&#xff0c;那么接下来需要考虑的就是如何确定这个对象是被谁持有导致泄漏的呢&#xff1f; 从内存泄漏一文中可知&#xff0c;当我们使用Android Studio或MAT分析内存泄…

vue3范围选择组件封装

个人项目地址&#xff1a; SubTopH前端开发个人站 &#xff08;自己开发的前端功能和UI组件&#xff0c;一些有趣的小功能&#xff0c;感兴趣的伙伴可以访问&#xff0c;欢迎提出更好的想法&#xff0c;私信沟通&#xff0c;网站属于静态页面&#xff09; SubTopH前端开发个人站…

测试框架pytest教程(10)自定义命令行-pytest_addoption

pytest_addoption pytest_addoption是pytest插件系统中的一个钩子函数&#xff0c;用于向pytest添加自定义命令行选项。 在pytest中&#xff0c;可以使用命令行选项来控制测试的行为和配置。pytest_addoption钩子函数允许您在运行pytest时添加自定义的命令行选项&#xff0c;…

四信智能充电桩解决方案

新能源汽车是信息技术与制造体系的全面融合&#xff0c;是产业发展的大势所趋&#xff0c;也是新动能的重要支点&#xff0c;而推进充电基础设施建设则是实现我国从汽车大国迈向汽车强国必由之路战略举措的有力保障。 据国际能源署测算&#xff0c;2030年全球私人充电桩保有量预…

数组和指针的练习解析(4)

题目&#xff1a; int main() { int aa[2][5] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *ptr1 (int *)(&aa 1); int *ptr2 (int *)(*(aa 1)); printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1)); return 0; } 思路分析&#xff1a; int *ptr1 (int *)(&…

Java的锁大全

Java的锁 各种锁的类型 乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念&#xff0c;体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。 先说概念。对于同一个数据的并发操作&#xff0c;悲观锁认为自己在使用数据的时候一定有别的线程来修改数…

AMBA总线协议(0)——目录与传送门

一、AMBA总线协议 Arm高级微控制器总线架构&#xff08;Advanced Microcontroller Bus Architecture&#xff0c;AMBA&#xff09;是一种开放式标准片上互联规范&#xff0c;用于连接和管理片上系统&#xff08;System on Chip,Soc&#xff09;中的功能块。 AMBA是一种广泛用于…

跨域知识点+springboot解决跨域

目录 一 跨域简介 二 跨域的三种情况 三 springboot解决跨域案例 一 跨域简介 什么是跨域&#xff1f; 由于浏览器的一些安全性限制&#xff0c;不允许前端页面访问一些协议不同、域名不同、端口号不同的http接口&#xff0c;例如我本地创建一个html&#xff0c;里面写一个a…

什么是代码审计?怎么做?

代码审计是对源代码进行人工或自动化审查&#xff0c;以查找潜在的安全漏洞和隐患。在信息安全测试中&#xff0c;代码审计是非常重要的一环。它主要包括以下几个方面&#xff1a; 1.变量验证&#xff1a;检查代码是否对变量进行验证&#xff0c;防止变量被恶意用户篡改。 2.…

python systemrdl 使用实例

今天来看一个具体实例&#xff0c;上一篇传送门&#xff1a;python SystemRDL 包介绍_Bug_Killer_Master的博客-CSDN博客 通常来说&#xff0c;我们验证过程用到的情况大多都是需要提取reg field的路径以及reset 值等信息&#xff0c;所以比较常见的一种方法就是先把rdl compil…

深入理解python虚拟机:调试器实现原理与源码分析

调试器是一个编程语言非常重要的部分&#xff0c;调试器是一种用于诊断和修复代码错误&#xff08;或称为 bug&#xff09;的工具&#xff0c;它允许开发者在程序执行时逐步查看和分析代码的状态和行为&#xff0c;它可以帮助开发者诊断和修复代码错误&#xff0c;理解程序的行…

vue(element ui安装)

目录 一&#xff0c;element ui安装二&#xff0c;main.js三&#xff0c;使用element ui最后 一&#xff0c;element ui安装 先在盘服中找到你创建的node的位置 如有不懂根据可以看看上一章安装node 然后在终端找到 进入这个位置之后就可以安装了 输入npm i element-ui -S这个…

什么是长短时记忆网络(LSTM)

什么是长短时记忆网络(LSTM) RNN有一定的记忆能力&#xff0c;但不幸的是它只能保留短期记忆&#xff0c;在各类任务上表现并不好&#xff0c;那该怎么办? 人们将目光投向了自己&#xff0c;人类的记忆是有取舍的&#xff0c;我们不会记住每时每刻发生的所有事&#xff0c;会…