【JAVA多线程】JDK线程同步工具:Semaphore、CountDownLatch、CyclicBarrier

目录

1.可能会遇到的线程协作场景

2.Semaphore

3.CountDownLatch

4.CyclicBarrier


1.可能会遇到的线程协作场景

在并发编程中,线程除了独自向前运行,还可能相互之间要进行协作,以保证完成最终总的目标。可能会遇到的几种任务之间的协作:

  • 情景1:限定任务数

    • 由于资源有限,限制最多有多少个线程进行工作

  • 情景2:任务之间有依赖关系

    • 一个线程依赖于其它线程的执行结果,这个线程就必须等待其它线程执行完成才能继续往下走

  • 情景3:任务分阶段

    • 批量线程分阶段执行,每一个阶段是一个同步点,执行完的线程必须阻塞在同步点上等待同批的其它线程也执行完,再进入下一个阶段。由于阶段可能有多个,所以要用condition来实现。

  • 情景4:动态调整线程数量

    • 不管是情景2也好还是情景3也好,都有可能有动态调整线程的可能性

2.Semaphore

semaphore,信号量,用来解决情景1。

业务情景:

我们有一个资源,只允许最多 3 个线程同时访问。我们将使用 Semaphore 来实现这一功能。

代码示例:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
​
public class SemaphoreExample {
​private static final int MAX_CONCURRENT_ACCESS = 3;private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_ACCESS);
​public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {try {// 获取许可semaphore.acquire();System.out.println(Thread.currentThread().getName() + " 开始访问资源");// 模拟耗时操作TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName() + " 结束访问资源");} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println(Thread.currentThread().getName() + " 被中断");} finally {// 释放许可semaphore.release();}}).start();}}
}

semaphore的实现底层其实就是AQS,没抢到资源的线程阻塞在队列中,也分了公平锁和非公平锁,其实现了AQS的共享模式tryAcquireShared(),每次去抢占资源的时候就对state做CAS减法。

acquire()去抢资源,没抢到或者抢失败了就把线程阻塞进CLH队列中:

最终调用到的是tryAcquireShared(),每次去抢占资源的时候就对state做CAS减法:

释放release()就不展开了,也是很简单的。

3.CountDownLatch

CountDownLatch,栅栏,用来解决情景2。

业务场景:

1个主线程需要等待10Worker线程完成工作才能退出。

这时候就要用CountDownLatch:

CountDownLatch countDownLatch=new CountDownLatch(10);
countDownLatch.await();//主线程阻塞在这里

其余Worker线程各自去:

countDownLatch.countDown();//每调用一次countDown,计数就会减1,减到0的时候主线程会被唤醒

countDownLatch也有一个继承AQS的Sync,countDown会去调用AQS共享模式的释放方法releaseShared()

releaseShared会CAS去对state进行-1,当发现state减到0后,会用doReleaseShared唤醒躺在CLH队列中的调用过await()的主线程:

4.CyclicBarrier

业务场景:

一共有10个线程,分阶段执行任务,每一个阶段必须所有10个线程都执行后,才能一同去执行下一个阶段的任务。

代码示例:

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
​
public class CyclicBarrierDemo {
​public static void main(String[] args) throws InterruptedException {// 创建一个 CyclicBarrier 实例CyclicBarrier barrier = new CyclicBarrier(10, () -> {System.out.println("All threads have arrived at the barrier. Moving to the next phase.");});
​// 启动 10 个线程for (int i = 0; i < 10; i++) {new Thread(() -> work(barrier)).start();}
​// 等待所有线程完成第一个阶段TimeUnit.SECONDS.sleep(2);
​// 等待所有线程完成第二个阶段TimeUnit.SECONDS.sleep(2);
​// 等待所有线程完成第三个阶段TimeUnit.SECONDS.sleep(2);}
​private static void work(CyclicBarrier barrier) {try {// 模拟工作TimeUnit.SECONDS.sleep(1); // 暂停一段时间
​// 到达屏障barrier.await();
​// 模拟第二阶段的工作TimeUnit.SECONDS.sleep(1); // 暂停一段时间
​// 到达屏障barrier.await();
​// 模拟第三阶段的工作TimeUnit.SECONDS.sleep(1); // 暂停一段时间
​// 到达屏障barrier.await();} catch (InterruptedException | BrokenBarrierException e) {Thread.currentThread().interrupt();System.err.println(Thread.currentThread().getName() + ": Interrupted or barrier broken.");}}
}

上面的代码模拟了三阶段的任务,没执行完一个阶段的任务线程就会调用CyclicBarrier的await()来等待其它的合作伙伴线程,要大家都达到后才会继续向下执行。可以看到CyclicBarrier的await()是线程同步的核心方法。一起来看看源码:

await()里面调用了doawait(),所以doawait才是核心方法:

来看看doawait()的源码:

private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;lock.lock();try {// 获取当前的 Generation 对象final Generation g = generation;
​// 如果屏障已经损坏,则抛出 BrokenBarrierExceptionif (g.broken)throw new BrokenBarrierException();
​// 如果线程被中断,则破坏屏障并抛出 InterruptedExceptionif (Thread.interrupted()) {breakBarrier();throw new InterruptedException();}
​// 减少 count 计数器的值,表示有一个线程到达了屏障int index = --count;// 如果 count 变为 0,这意味着所有线程都已经到达屏障if (index == 0) {  // trippedboolean ranAction = false;try {// 如果设置了屏障动作(回调函数),则执行该动作final Runnable command = barrierCommand;if (command != null)command.run();ranAction = true;// 开始新的屏障周期nextGeneration();// 返回 0 表示当前线程触发了屏障动作return 0;} finally {// 如果未执行屏障动作(回调函数),则破坏屏障if (!ranAction)breakBarrier();}}
​// 循环等待其他线程到达for (;;) {//阻塞在condition上(trip是个condition),这样就能将lock释放出来,后面的线程可以继续争抢try {// 如果没有设置超时时间,则等待所有线程到达if (!timed)trip.await();// 如果设置了超时时间,则等待所有线程到达或超时else if (nanos > 0L)nanos = trip.awaitNanos(nanos);} catch (InterruptedException ie) {// 如果线程被中断且屏障仍然有效,则破坏屏障并抛出 InterruptedExceptionif (g == generation && ! g.broken) {breakBarrier();throw ie;} else {// 如果线程即将完成等待,即使它被中断,也会忽略中断标志并将中断标记传递给后续执行Thread.currentThread().interrupt();}}
​// 如果屏障已经损坏,则抛出 BrokenBarrierExceptionif (g.broken)throw new BrokenBarrierException();
​// 如果屏障周期已经改变,则返回当前线程的索引if (g != generation)return index;
​// 如果设置了超时并且超时时间已到,则破坏屏障并抛出 TimeoutExceptionif (timed && nanos <= 0L) {breakBarrier();throw new TimeoutException();}}} finally {// 释放锁lock.unlock();}
}

其实可以看到上面的逻辑很简单,要是没到齐就先阻塞等待,要是到齐了就调用nextGeneration()去刷新轮次,这个方法里也就是一些资源的重置。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法知识点————背包问题【动态规划】【打家劫舍】

万能头文件#include<bits/stdc.h> 01 背包 定义&#xff1a; 物品只能用1次。01对应选还是不选第i个物品 .N个物品、V容量的最大价值。 思路&#xff1a; &#xff08;1&#xff09;f[ i ] [j] 表示前i个物品容量j的最大价值。 &#xff08;2&#xff09;当前背包容量…

中国人民银行:数字人民币交易额已达7万亿元!中俄考虑使用国家数字货币进行双边结算!

近年来&#xff0c;数字货币的迅速发展引起了全球的广泛关注。中国人民银行&#xff08;PBOC&#xff09;近日透露&#xff0c;数字人民币&#xff08;e-CNY&#xff09;的交易额已接近1万亿美元&#xff0c;这标志着中国在数字货币领域的重大进展。同时俄罗斯也表示&#xff0…

file | 某文件夹【解耦合】下的文件查找功能实现及功能单元测试

文件查找工具 概要思路OS模块 --- 学习版os.getcwd()os.path.dirname(os.getcwd())os.path.dirname() 和 os.path.basename() OS模块 — 实战版单元测试解耦合 概要 梳理业务主逻辑&#xff1a; 查看存放被采集JSON数据的文件夹内的文件列表【所有 包含文件夹下的文件夹下的文…

C语言 | Leetcode C语言题解之第395题至少有K个重复字符的最长子串

题目&#xff1a; 题解&#xff1a; int longestSubstring(char* s, int k) {int ret 0;int n strlen(s);for (int t 1; t < 26; t) {int l 0, r 0;int cnt[26];memset(cnt, 0, sizeof(cnt));int tot 0;int less 0;while (r < n) {cnt[s[r] - a];if (cnt[s[r] - …

论文阅读:3D Gaussian Splatting for Real-Time Radiance Field Rendering

论文地址&#xff1a;https://arxiv.org/abs/2308.04079 代码地址&#xff1a;graphdeco-inria/gaussian-splatting: Original reference implementation of "3D Gaussian Splatting for Real-Time Radiance Field Rendering" (github.com) 概要 提出一个实时且能够…

论文解读 | ACL2024 Outstanding Paper:因果指导的主动学习方法:助力大语言模型自动识别并去除偏见...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击阅读原文观看作者直播讲解回放&#xff01; 作者简介 孙洲浩&#xff0c;哈尔滨工业大学SCIR实验室博士生 概述 尽管大语言模型&#xff08;LLMs&#xff09;展现出了非常强大的能力&#xff0c;但它们仍然…

ApplicationVerifier介绍说明

文章目录 1、介绍1、安装2、配置需要验证的项目2、在WinDbg中调试3、其他配置项 1、介绍 AppVerifier 特别用于检测和帮助调试内存损坏、危险的安全漏洞以及受限的用户帐户特权问题。 AppVerifier 有助于创建可靠且安全的应用程序&#xff0c;方法是监视应用程序与Windows操作…

53 - I. 在排序数组中查找数字 I

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9853%20-%20I.%20%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E6%95%B0%E5%AD%97%20I/README.md 面试题 53 - I. 在排序数组中查找数字 …

Mysql基础练习题 1757.可回收且低脂的产品(力扣)

编写解决方案找出既是低脂又是可回收的产品编号。 题目链接&#xff1a; https://leetcode.cn/problems/recyclable-and-low-fat-products/description/ 建表插入数据&#xff1a; Create table If Not Exists Products (product_id int, low_fats ENUM(Y, N), recyclable …

mysql 之 information_schema

information_schema 是 MySQL 中的一个特殊数据库&#xff0c;它提供了关于 MySQL 服务器中所有数据库、表、列、索引、存储过程、函数、触发器等对象的元数据信息。information_schema 是一个只读数据库&#xff0c;主要用于查询数据库的结构信息&#xff0c;而不是存储用户数…

【网络安全】-文件上传漏洞

文件操作漏洞包括文件上传漏洞&#xff0c;文件包含漏洞&#xff0c;文件下载漏洞。 文章目录 前言 什么是文件上传漏洞&#xff1f; 文件上传的验证与绕过&#xff1a; 1.前端js验证&#xff1a;   Microsft Edge浏览器&#xff1a; Google Chrome浏览器&#xff1a; 2.后端…

[WEBPWN]BaseCTF week1 题解(新手友好教程版)

WEB A Dark Room 这道题的考点是查看网页源代码 网页源代码这里看到的是网页的html css js在用户浏览器上执行的代码 有时候很多铭感信息&#xff0c;或者关键信息。 查看网页源代码的几种方式 1 右键点击查看网页源代码 2 F12 3 Ctrl U 快捷键 HTTP是什么 HTTP&#x…

【F179】基于Springboot+vue实现的幼儿园管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 系统管理也都将通过计算机进行整体智能化操作&#xff…

Redis学习Day3——项目工程开发`

扩展阅读推荐&#xff1a; 黑马程序员Redis入门到实战教程_哔哩哔哩_bilibili 使用git命令行将本地仓库代码上传到gitee/github远程仓库-CSDN博客 一、项目介绍及其初始化 学习Redis的过程&#xff0c;我们还将遇到各种实际问题&#xff0c;例如缓存击穿、雪崩、热Key等问题&…

IGNAV_NHC分析

extern int nhc(insstate_t *ins,const insopt_t *opt,const imud_t *imu)函数名 insstate_t* ins IO ins state insopt_t* opt I ins options imud_t* imu I imu measurement data return : 1 (ok) or 0 (fail) 用NHC进行约束&#xff0c;其实用NHC做量测去…

从大脑图谱/ROI中提取BOLD信号

动机 在功能连接&#xff08;Functional Connectivity&#xff0c;FC&#xff09;构建过程中&#xff0c;由于FC中元素数目是节点数目的平方关系&#xff0c;所以在计算FC之前进行数据降维是一个常见的选择。 一般会将体素级/顶点级BOLD信号&#xff08;在2mm的图像分辨率下大脑…

Android libui新加接口,编译报错:error: Please update ABI references

1.背景信息 由于项目需要,要合入google的bug fix:https://cs.android.com/android/_/android/platform/frameworks/native/+/2c1782c6f986debe5ec89d5cdd3a3f08b08d5683 查看google的修改发现,对Transform.h 增加了一个方法:android::ui::Transform::det。合入修改之后,我…

NXP,S32K1XX汽车通用微控制器开发笔记

文章目录 1. 概述2. 开发环境配置2.1 S32 Design Studio2.2 安装SDK2.3 新建demo工程2.4 字体配置2.5 按需求修改demo2.5.1 修改pin脚定义2.5.2 增加串口打印功能2.6 编译代码2.7 debuger 配置参考1. 概述 S32K1系列32位微控制器(MCU)提供基于Arm Cortex-M的MCU,以及基本的…

pycharm中函数或方法的跳转以及返回

跳转 跳转很方便&#xff0c;ctrl 函数名即可。 跳转返回 有自带的回退按钮&#xff0c;找到视图->外观->工具栏&#xff0c;选中工具栏&#xff0c;这样就能出现箭头按钮&#xff0c;左箭头就是回退&#xff0c;右箭头前进。 快捷按钮可以为&#xff1a; 回退&…

Docker高级管理之compose容器编排与私有仓库的部署

Compose容器编排 Compose&#xff1a;容器的编排技术&#xff08;可以管理多个容器&#xff09;&#xff0c;移植性、迁移性更强 查看使用的Compose的版本&#xff1a;docker-compose -v 首先创建一个编排文件 文件内容 compose文件格式&#xff1a; 缩进&#xff08;严格意…