JAVA并发编程入门之-闭锁、信号量、栅栏

文章目录

  • 一、闭锁
    • CountDownLatch
    • FutureTask
  • 二、信号量
    • Semaphore
  • 三、栅栏(Barrier)
    • CyclicBarrier(循环栅栏)

一、闭锁

闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态;闭锁的作用相当于一扇门,在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有线程通过;当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态;
闭锁是一次性对象,一旦进入终止状态就不能被重置;
闭锁可以用来确保某些活动直到其它活动都完成后才继续执行;适用场景:

应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行;
多玩家游戏中当所有玩家都就绪后才执行某项活动;
设想有这样一个功能需要Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给主线程去做汇总,利用闭锁来完成就非常轻松;
闭锁状态包括一个计数器,该计数器被初始化为一个整数,表示需要等待的事件数量;

CountDownLatch

CountDownLatch是闭锁的一种实现;CountDownLatch是在java1.5被引入;

CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行;

public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}

主要API:

countDown():该方法递减计数器,表示有一个事件已经发生;
await():该方法等待计时器达到零,达到零后表示需要等待的所有事件都已发生;
如果计数器的值非零,await方法会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时;

起始门(Starting Gate)
所有子线程等待计数器为零后一起执行

public class Appliction {private final static int NUM = 10;public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(1);for (int i = 0; i < NUM; i++) {new Thread(() -> {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.err.println(Thread.currentThread().getName() + " started:" + System.currentTimeMillis());}).start();}countDownLatch.countDown();System.err.println("main thread exec end");}
}

结束门(Ending Gate)
等待所有子任务或子线程结束后(计数器为零),对执行结果进行统计或汇总

/*** 假设有10块磁盘,需要10个线程同时统计磁盘空间大小,统计完成后由主线程进行汇总*/
public class Appliction {private final static int NUM = 10;public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(NUM);List<Disk> tasks = new ArrayList<>(NUM);for (int i = 0; i < NUM; i++) {tasks.add(new Disk(i));}for (Disk dk : tasks) {new Thread(new DiskCountTask(countDownLatch, dk)).start();}countDownLatch.await();int size = tasks.stream().mapToInt(Disk::getSize).sum();System.err.println("All disk space size:" + size);}
}class Disk {private Integer size;public Disk(Integer size) {this.size = size;}public Integer getSize() {return size;}public void setSize(Integer size) {this.size = size;}
}
class DiskCountTask implements Runnable {private Disk disk;private CountDownLatch downLatch;public DiskCountTask(CountDownLatch downLatch, Disk disk) {this.downLatch = downLatch;this.disk = disk;}@Overridepublic void run() {int size = disk.getSize();try {TimeUnit.SECONDS.sleep(size);} catch (InterruptedException e) {e.printStackTrace();}System.err.println(Thread.currentThread().getName() + " exec end[" + System.currentTimeMillis() + "], size:" + size);downLatch.countDown();System.out.println("count:"+downLatch.getCount());}
}

FutureTask

FutureTask也可以用作闭锁;

FutureTask实现了Future语义,表示一种抽象的可生成结果的计算;Future表示的计算是通过Callable来实现,相当于一种可生成结果的Runnable,并且可处于以下三种状态:等待运行,正在运行(Running)和运行完成(Completed);
FutureTask.get的行为取决于任务的状态;如果任务已经完成,那么get会立即返回结果,否则将阻塞直到任务进入完成状态,然后返回结果或抛出异常;
FutureTask将计算结果从执行计算的线程传递到获取这个结果的线程,而FutureTask的规范确保了这种传递过程能实现结果的安全发布;

示例:

public class Appliction {private final static int NUM = 10;public static void main(String[] args) throws InterruptedException {List<FutureTask<Integer>> taskList = new ArrayList<>();ExecutorService executorService = Executors.newFixedThreadPool(NUM);for (int i = 0; i < NUM; i++) {FutureTask<Integer> task = new FutureTask<>(new DiskFutureTask(new Disk(i)));taskList.add(task);executorService.execute(task);}executorService.shutdown();int result = 0;for (FutureTask<Integer> task : taskList) {Integer size = task.get();System.out.println("get size:"+size);result += size;}System.err.println("All disk space size:" + result);}
}class Disk {private Integer size;public Disk(Integer size) {this.size = size;}public Integer getSize() {return size;}public void setSize(Integer size) {this.size = size;}
}
class DiskFutureTask implements Callable<Integer> {private Disk disk;public DiskFutureTask(Disk disk) {this.disk = disk;}@Overridepublic Integer call(){Integer size = disk.getSize();try {TimeUnit.SECONDS.sleep(disk.getSize());} catch (InterruptedException e) {e.printStackTrace();}System.err.println(Thread.currentThread().getName() + " exec end[" + System.currentTimeMillis() + "], size:" + size);return size;}
}

二、信号量

计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量;

Semaphore

Semaphore管理着一组虚拟许可(permit),许可的初始数量可通过构造函数来指定;在执行操作时先获取许可(只要还有剩余的许可),使用完成后释放许可;如果没有许可那么acquire将阻塞直到有许可(或者被中断或操作超时);release方法将返回一个许可给信号量;

初始值为1的Semaphore称为二值信号量;二值信号量可以用作互斥体(mutex),并具备不可重入的加锁语义;谁拥有这个唯一的许可谁就拥有了互斥锁;

使用场景:

Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来

/*** 假设公司体检,房间里一共有3位体检医师,所以一次可以进入3个人,有人出来就有人可以进去*/
public class Appliction {public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {Semaphore semaphore = new Semaphore(3);for (int i = 0; i < 100; i++) {new Thread(new PhysicalExaminationTask(semaphore)).start();}}
}class PhysicalExaminationTask implements Runnable{private Semaphore semaphore;public PhysicalExaminationTask(Semaphore semaphore){this.semaphore = semaphore;}@Overridepublic void run() {try {semaphore.acquire();} catch (InterruptedException e) {e.printStackTrace();}int time = new Random().nextInt(5);if (time > 0) {try {TimeUnit.SECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}System.err.println(Thread.currentThread().getName() + " finished");semaphore.release();}
}

三、栅栏(Barrier)

栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生;

栅栏与闭锁的关键区别:所有线程必须同时达到栅栏位置,才能继续执行;闭锁用于等待事件,而栅栏用于等待其它线程;

CyclicBarrier(循环栅栏)

CyclicBarrier可以使一定数量的参与方反复地在栅栏位置汇集

CyclicBarrier初始化的时候,设置一个屏障数。线程调用await()方法的时候,这个线程就会被阻塞,当调用await()的线程数量到达屏障数的时候,主线程就会取消所有被阻塞线程的状态

示例

/*** 三个人一起爬山,达到第一集合点后必须等到所有人员到齐后再开始往第二集合点进发*/
public class Appliction {private static final int NUM = 3;public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(NUM);String phaseName = "phase one";for (int i = 0; i < NUM; i++) {new Thread(new PhaseTask(phaseName, cyclicBarrier)).start();}}
}class PhaseTask implements Runnable {private String phaseName;private CyclicBarrier cyclicBarrier;public PhaseTask(String phaseName, CyclicBarrier cyclicBarrier) {this.phaseName = phaseName;this.cyclicBarrier = cyclicBarrier;}@Overridepublic void run() {sleep();System.err.println(Thread.currentThread().getName() + "到达第一个集合点");waitFor();sleep();System.err.println(Thread.currentThread().getName() + "到达第二个集合点");waitFor();}private void sleep() {int time = new Random().nextInt(10);if (time > 0) {try {TimeUnit.SECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}}private void waitFor() {try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}
}

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

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

相关文章

SSM电影售票管理系统----计算机毕业设计

项目介绍 管理员角色包含以下功能&#xff1a; 管理员登陆,管理员用户管理,新闻公告增删改查,电影类型增删改查,影院信息增删改查,电影信息增删改查,订单查询,电影评价管理等功能。 用户角色包含以下功能&#xff1a; 用户首页,用户登录,查看电影详情,加入购物车,下单电影票,…

数据结构:图详解

图的存储方式 邻接矩阵 首先先创建图&#xff0c;这一个我们可以使用邻接矩阵或者邻接链 表来进行存储&#xff0c;我们要实现的无向图的创建&#xff0c;我们先创建 一个矩阵尺寸为n*n&#xff0c;n为图中的节点个数如图所示 可以看出图中有5个结点&#xff0c;那我们创建…

基于web3.js和ganache实现智能合约调用

目的&#xff1a;智能合约发布到本地以太坊模拟软件ganache并完成交互 准备工作&#xff1a; web3.jsganache模拟软件 ganache参数配置 从ganache获取一个url&#xff0c;和一个账号的地址&#xff0c; url直接使用图中的rpc server位置的数据即可 账号address从下列0x开头…

【Java】java -jar 读取jar包之外的yml

需求描述 springboot项目接入nacos配置&#xff0c;代码中使用bootstrap.yml来指定nacos信息&#xff0c;为了防止不同环境的来回切换&#xff0c;服务器中都单独在放一个bootstrap.yml&#xff0c;来指定具体环境的nacos配置&#xff0c;如sit服务器使用sit的nacos配置&#…

SpringBoot中动态注册接口

1. 说明 接口注册&#xff0c;使用RequestMappingHandlerMapping来实现mybatis中动态执行sql使用github上的SqlMapper工具类实现 2. 核心代码片段 以下代码为spring动态注册接口代码示例 Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping;publ…

进程控制-操作系统

1. 进程概述 进程和程序的区别:程序和进程是两个不同的概念&#xff0c;他们的状态&#xff0c;占用的系统资源都是不同的。 程序&#xff1a;就是磁盘上的可执行文件文件, 并且只占用磁盘上的空间&#xff0c;是一个静态的概念。进程&#xff1a;被执行之后的程序叫做进程&a…

c++和c语言动态内存分配

动态开辟内存,就是在堆区,可以根据自己的需要开辟和释放。(用完一定得释放) 1.C语言 malloc和free 1. (void*)malloc(size_t n); // 传入一个需要开辟多少个字节的内存 (可以是一个常数,也可以是一个可以计算出的表达式)&#xff0c;返回一个void*指针指向开辟内存的首地址…

Maven之依赖的传递

问题导入 1. 依赖传递 A依赖B&#xff0c;B依赖C&#xff0c;A是否依赖于C呢&#xff1f;–A依赖于C 依赖具有传递性 路径优先&#xff1a;当依赖中出现相同的的资源时&#xff0c;层级越深&#xff0c;优先级越低&#xff0c;层级越浅&#xff0c;优先级越高 声明优先&…

nacos 2.* 部署在linux服务器无法注册问题

通过sdk注册代码 报错 Exception in thread "main" ErrCode:-401, ErrMsg:Client not connected, current status:STARTING at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:639) at com.alibaba.nacos.common.remote.client…

【网络编程】——基于TCP协议实现回显服务器及客户端

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】【Java系列】 本专栏旨在分享学习网络编程的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、TCP实…

AI无人直播系统怎么样?三点说明

近年来&#xff0c;因为科技的高速进步&#xff0c;不断涌现出了越来越多的新技术和创新事物&#xff0c;它们以其独特的方式取代了我们的许多传统做法&#xff0c;从而彻底解放了我们的双手。在这股潮流中&#xff0c;无人直播作为一种创新形式&#xff0c;使得直播变得更加简…

安装部署halo博客

Docker 安装文档&#xff1a;https://docs.docker.com/engine/install/ Docker Compose 安装文档&#xff1a;https://docs.docker.com/compose/install/ mkdir ~/halo && cd ~/halotouch ~/halo/docker-compose.yamlvim application.yaml application.yaml version:…

时间序列系列04-时间序列间因果关系

挖掘时间序列间的因果关系是时间序列分析中的一个重要任务&#xff0c;它有助于理解变量之间的动态关系、预测未来趋势以及发现潜在的影响因素。以下是一些常用的方法和技术&#xff1a; 多维时间序列变量的因果推断 1. 格兰杰因果关系检验&#xff08;Granger Causality Test…

[go 面试] 分布式事务框架选择与实践

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等&#xff0c;您的关注将是我的更新动力&#xff01; 分布式事务是处理跨多个服务的原子操作的关键概念&#xff0c;而选择适合应用场景的框架对于确保事务一致性至关重要。以下是几个…

【容器】K8s RBAC介绍

认识RBAC RBAC&#xff08;基于角色的访问控制&#xff09;是一种将权限分配给用户和服务的方法&#xff0c;基于他们的角色来确定他们可以访问和修改的资源。K8s使用RBAC作为来访请求鉴权的机制之一。 场景&#xff1a;访问K8s接口时的认证和鉴权 某些场景下&#xff0c;我…

面试算法98:路径的数目

题目 一个机器人从mn的格子的左上角出发&#xff0c;它每步要么向下要么向右&#xff0c;直到抵达格子的右下角。请计算机器人从左上角到达右下角的路径的数目。例如&#xff0c;如果格子的大小是33&#xff0c;那么机器人从左上角到达右下角有6条符合条件的不同路径。 分析…

rabbitmq延时队列相关配置

确保 RabbitMQ 的延时消息插件已经安装和启用。你可以通过执行以下命令来安装该插件&#xff1a; rabbitmq-plugins enable rabbitmq_delayed_message_exchange 如果提示未安装&#xff0c;以下是安装流程&#xff1a; 查看mq版本&#xff1a; 查看自己使用的 MQ&#xff08;…

全网最全丨傻瓜式Fiddler教程大全丨从安装到抓包

前言 在我们做接口测试的时候&#xff0c;经常需要验证发送的消息是否正确&#xff0c;或者在出现问题的时候&#xff0c;查看手机客户端发送给server端的包内容是否正确&#xff0c;就需要用到抓包工具。 今天&#xff0c;给大家带来最常用的Fiddler的傻瓜式教程大全——从安…

Python中的并发编程技术与实践分享

一、并发编程的概念与重要性 并发编程是计算机科学中的一个重要概念&#xff0c;它涉及到在同一时间内处理多个任务的能力。在多核CPU和云计算日益普及的今天&#xff0c;并发编程变得越来越重要。通过并发编程&#xff0c;我们可以充分利用系统资源&#xff0c;提高程序的执行…

九、综合实例:修改用户资料(Qt5 GUI系列)

目录 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 设计一个修改用户资料功能的对话框&#xff0c;要求包含基本信息、联系方式、详细资料的编辑和修改。本实例只实现界面。 二、实现代码 导航页面&#xff1a; //添加的头文件 #include <QStacked…