作者:CoderV的进阶笔记

https://juejin.cn/post/6844903958360621064

CountDownLatch

解释:

CountDownLatch相当于一个门闩,门闩上挂了N把锁。只有N把锁都解开的话,门才会打开。怎么理解呢?我举一个赛跑比赛的例子,赛跑比赛中必须等待所有选手都准备好了,裁判才能开发令枪。选手才可以开始跑。

CountDownLatch当中主要有两个方法,一个是await()会挂上锁阻塞当前线程,相当于裁判站在起始点等待,等待各位选手准备就绪,一个是countDown方法用于解锁,相当于选手准备好了之后调用countDown方法告诉裁判自己准备就绪,当所有人都准备好了之后裁判开发令枪。

代码:

public class TestCountDownLatch {public static void main(String[] args) {// 需要等待两个线程,所以传入参数为2CountDownLatch latch = new CountDownLatch(2);// 该线程运行1秒new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("1号选手准备就绪!用时1秒!");latch.countDown();}}).start();// 该线程运行3秒new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("2号选手准备就绪!用时3秒!");latch.countDown();}}).start();try {System.out.println("请1号选手和2号选手各就各位!");// 主线程在此等待两个线程执行完毕之后继续执行latch.await();} catch (InterruptedException e) {e.printStackTrace();}// 两个线程执行完毕后,主线程恢复运行System.out.println("裁判发枪,1号选手和2号选手开跑!");}
}

运行结果:

请1号选手和2号选手各就各位!
1号选手准备就绪!用时1秒!
2号选手准备就绪!用时3秒!
裁判发枪,1号选手和2号选手开跑!

如果去掉CountDownLatch的效果呢?运行结果就会变成如下:

请1号选手和2号选手各就各位!
裁判发枪,1号选手和2号选手开跑!
1号选手准备就绪!用时1秒!
2号选手准备就绪!用时3秒!

裁判就会在选手还未准备就绪的时候开发令枪,这就乱套了。
其实CountDownLatch一个最简单的用处就是计算多线程执行完毕时的时间。像刚才的例子当中两个线程并行执行了共花费了3秒钟。

CyclicBarrier

解释:

CyclicBarrier就像一个栅栏,将各个线程拦住。Cyclic是循环的英文,表明该工具可以进行循环使用。CyclicBarrier(N)的构造参数表明该一共有几个线程需要互相等待。它相当于N个选手约定进行多次比赛,每次比赛完都要在起跑点互相等待。

读者可能会马上疑惑这不是和CountDownLatch一样吗?不一样。因为CountDownLatch是裁判等待选手,是调用await()方法的线程,等待调用countDown()方法的各个线程。而CyclicBarrier是选手等待选手,是调用await()方法的线程互相等待,等待其他线程都运行好之后,再开始下一轮运行。

我们举一个例子,两个选手进行比赛,一共进行三轮比赛。

代码:

public class TestCyclicBarrier {// 1号选手跑的轮数public static int countA = 1;// 2号选手跑的轮数public static int countB = 1;public static void main(String[] args) {// 填入2,代表2个线程互相等待CyclicBarrier barrier = new CyclicBarrier(2);new Thread(new Runnable() {@Overridepublic void run() {// 一共跑三轮for (int i = 0; i < 3; i++) {System.out.println("1号选手开始跑!当前第" + countA++ + "轮比赛!");// 1号选手跑得慢,每次跑三秒try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}try {System.out.println("1号选手抵达终点!");// 调用等待方法,在此等待其他选手barrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {// 一共等待三轮for (int i = 0; i < 3; i++) {System.out.println("2号选手开始跑!当前第" + countB++ + "轮比赛!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {System.out.println("2号选手抵达终点!");// 调用等待方法,在此等待其他选手barrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}}}).start();}}

运行结果:

1号选手开始跑!当前第1轮比赛!
2号选手开始跑!当前第1轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第2轮比赛!
2号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第3轮比赛!
2号选手开始跑!当前第3轮比赛!
2号选手抵达终点!
1号选手抵达终点!

每轮比赛1号选手和2号选手都会回到起跑线互相等待,再开启下一轮比赛。

如果不加CyclicBarrier呢?

1号选手开始跑!当前第1轮比赛!
2号选手开始跑!当前第1轮比赛!
2号选手抵达终点!
2号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
2号选手开始跑!当前第3轮比赛!
1号选手抵达终点!
1号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第3轮比赛!
1号选手抵达终点!

此时2号选手就直接跑完三轮比赛,不等1号选手了。

Semaphore

Semaphore英文的字面意思是信号量。它的工作机制是每个线程想要获取运行的机会的话,都必须获取到信号量。acquire()方法阻塞的获取信号量,release()释放信号量。

举个例子,假设我们去迪士尼游玩,但是迪士尼担心游客很多的话,影响大家的游玩体验,于是规定每个小时只能卖出两张门票。这样就可以控制在游乐园当中的游客数量了。

代码:

public class TestSemaphore {public static void main(String[] args) {Semaphore semaphore = new Semaphore(0);System.out.println("顾客在售票处等候中");new Thread(new Runnable() {@Overridepublic void run() {for (; ; ) {try {Thread.sleep(500);// 等待出票semaphore.acquire();System.out.println("顾客拿到门票入场!");} catch (InterruptedException e) {e.printStackTrace();}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 3; i++) {try {// 等待一小时再发门票Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 一次性发出两张门票System.out.println("售票处第" + (i + 1) + "小时售出两张票!");semaphore.release();semaphore.release();}}}).start();System.out.println("售票处开始售票!");}
}

运行结果:

顾客在售票处等候中...
售票处开始售票!
售票处第1小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!
售票处第2小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!
售票处第3小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!

Exchanger

解释:

Exchanger提供了让两个线程互相交换数据的同步点。Exchanger有点像2个线程的CyclicBarrier,线程之间都是互相等待,区别在于Exchanger多了交换的操作。

举个例子好比以前玩网游的时候,买家和卖家必须走到地图上同一地点面对面进行交易一样,一手交钱一手交装备。

代码:

public class TestExchanger {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(new Runnable() {@Overridepublic void run() {String weapon = "装备";System.out.println("我是卖家,我带着" + weapon + "过来了!");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("卖家到达地图上交易地点");try {System.out.println("我是卖家,换回了" + exchanger.exchange(weapon));} catch (InterruptedException e) {e.printStackTrace();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {String money = "一万游戏币";System.out.println("我是买家,我带着" + money + "过来了");try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("买家到达地图上交易地点");try {System.out.println("我是买家,换回了" + exchanger.exchange(money));} catch (InterruptedException e) {e.printStackTrace();}}}).start();}

运行结果:

我是卖家,我带着装备过来了!
我是买家,我带着一万游戏币过来了
卖家达到交易地点
买家到达交易地点
我是买家,换回了装备
我是卖家,换回了一万游戏币

推荐好文

>>【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能>>分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!
>>能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!

一文搞懂四种同步工具类相关推荐

  1. 一文搞懂四种 WebSocket 使用方式

    在上家公司做IM消息系统的时候,一直是使用 WebSocket 作为收发消息的基础组件,今天就和大家聊聊在 Java 中,使用 WebSocket 所常见的四种姿势,如果大家以后或者现在碰到有要使用 ...

  2. 一文看懂四种共享单车主流电子围栏技术

    共享单车之所以与中国的高铁,支付宝和网购一道,被老外称之为中国的新四大发明之一,就源于它以绿色环保,高效共享的方式解决了人们最后一公里出行问题.对比原来的有桩停放的"共享单车",现 ...

  3. 深入理解同步工具类,看这篇就够了

    大家好,我是陶朱公Boy. 今天跟大家分享一个并发编程领域中的一个知识点--同步工具类. 我将结合一个真实线上案例作为背景来展开讲解这一知识点.给大家讲清楚什么是同步工具类.适合的场景.解决了什么问题 ...

  4. JAVA同步工具类——CountDownLatch

    闭锁 在学习CountDownLatch之前,让我们先了解一下闭锁的概念. 闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态:闭锁的作用相当于一扇门,在闭锁到达结束状态之前,这扇门一直是关闭 ...

  5. LeetCode刷题复盘笔记—一文搞懂完全背包之322. 零钱兑换问题(动态规划系列第十四篇)

    今日主要总结一下动态规划完全背包的一道题目,322. 零钱兑换 题目:322. 零钱兑换 Leetcode题目地址 题目描述: 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amo ...

  6. 一文搞懂CAN FD总线协议帧格式

    目录 1.为什么会出现CAN FD? 2.什么是CAN FD? 3.CAN FD和CAN总线协议帧异同 4.解析CAN FD帧结构 4.1.帧起始 4.2.仲裁段 4.3.控制段 4.4.数据段 4. ...

  7. 一文搞懂 Traefik2.1 的使用

    原文链接:一文搞懂 Traefik2.1 的使用 一文搞懂 Traefik2.1 的使用 核心概念 安装 ACME 中间件 灰度发布 流量复制 TCP 简单 TCP 服务 带 TLS 证书的 TCP ...

  8. 一文搞懂UART通信协议

    目录 1.UART简介 2.UART特性 3.UART协议帧 3.1.起始位 3.2.数据位 3.3.奇偶校验位 3.4.停止位 4.UART通信步骤 1.UART简介 UART(Universal ...

  9. 一文搞懂MySQL数据库分库分表

    如果数据量过大,大家一般会分库分表.分库需要注意的内容比较少,但分表需要注意的内容就多了. 工作这几年没遇过数据量特别大的业务,那些过亿的数据,因为索引设置合理,单表性能没有影响,所以实战中一直没用过 ...

最新文章

  1. python数据结构与算法:栈
  2. “六成应用开发不需要程序员”喜提热搜背后,RPA 会抢饭碗吗?
  3. python iocp_记对协程增加IOCP支持时候踩过的一些坑
  4. Oracle版本号的含义
  5. SpringBoot入门教程(一)详解intellij idea搭建SpringBoot
  6. 纸盒叠成的长方形竟然能自己动?
  7. you need to resolve your current index first 解决办法
  8. 四元数与复数之间的关系
  9. 如何让cloudflare缓存html,wordpress博客使用CloudFlare的页面规则缓存设置教程
  10. 有意思的签到题集合~~
  11. 2022低压电工操作证考试题模拟考试平台操作
  12. 程序员windows基础操作系列文章目录
  13. 深入理解操作系统实验——bomb lab(phase_4)
  14. iPhone X 为啥弃 TouchID?
  15. IT行业最全的服务器硬件基础知识大全,值得收藏!
  16. SpringCloud版本Hoxton SR5 --- 第七讲:SpringCloud Config 分布式配置中心+整合bus、rabbitmq、actuator
  17. 麦当劳中国全年将招聘超18万人;印度两家仿制药企获准生产吉利德瑞德西韦 | 美通企业日报...
  18. Unity如何调用摄像头并显示在窗口内
  19. html5+css3布局尝试
  20. mac出现文件夹问号_Macbook文件夹怎么设置隐藏或显示?开机带问号的文件夹怎么办?...

热门文章

  1. 10月24日见!iQOO Neo 855版官宣:跑分不输骁龙855Plus
  2. 华为EMUI10的美学哲思:让美不止初见
  3. 把结构用二进制方式写入文件,并读出来[转]
  4. C语言教程第六章:指针(2)
  5. 在相册查看保存的图片
  6. 做菜不好吃,你一定是忽略了这20个小技巧!
  7. 晨哥真有料丨你喜欢人家啊,你追求人家。你到了什么地步就该放弃了?
  8. python二维向量运算模拟_Python数学基础之向量定义与向量运算(附代码)
  9. python调用ping命令_python调用系统命令ping
  10. c# 指定打开某个路径下的CMD_【自学C#】|| 笔记 25 文件的操作