你应该知道的四种并发工具类
前言
苏宁估计是今年最大的黑马了,我们在开始中没有期望能战胜G2小组第一出线,在八进四中我们也没有没想到苏宁能够打赢京东,结果苏宁3:1战胜京东,我们认为苏宁今年的世界赛的成绩已经很好了,或许也就止步于此了,毕竟他作为LPL三号种子,接下来要面对的是一号种子Tes,而且Tes刚经历了一场让二追三的血腥厮杀,状态火热。
苏宁用实力告诉你,不,我要追求更高的目标,他们战胜了Tes,成为了LPL 唯一的希望。
现在我们唯一的期待,10月31号,我们能听到一句恭喜苏宁,恭喜LPL!
这波,感谢一波大校,没问题吧!
周六大校支持一波大乌龟没问题吧
感谢你,苏小落
回归主题,今天给大家分享个简单的知识点,JDK并发包里面自带的一些工具类,功能很强大实用
CountDownLatch
CountDownLatch就像一个门,门上有N把锁,只有当锁同时都打开,我们才能开门。这里这个门是一次性的,用完之后不能重新再给这个门上锁,为什么我要强调一次性呢?因为下面还有不是一次性的CyclicBarrier
CountDownLatch,使一个线程等待其他线程都达到相应的状态再执行,完成工作的主要方法就是await()、countDown()
内部是通过一个计数器实现的,计数器的初始值就是要等待线程的数量,每当一个线程执行完毕调用countDown()之后计数器数值减一,计数器数值变为0的时候,表示所有线程执行完毕,调用await()方法等待的线程便会恢复工作
来看一个例子:
public class TestCountDownLatch {public static void main(String[] args) {// 传入参数2,代表需要等待两个线程final CountDownLatch latch = new CountDownLatch(2);//线程等待一秒new Thread(new Runnable() {public void run() {try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("一号准备完毕,用时1秒!");latch.countDown();}}).start();//线程等待三秒new Thread(new Runnable() {public void run() {try {Thread.sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("二号准备完毕,用时2秒!");latch.countDown();}}).start();try {System.out.println("两位选手请准备!");//主线程调用await,等待两个线程到达(即调用countDown)latch.await();} catch (InterruptedException e) {e.printStackTrace();}// 两个线程执行完毕后,主线程恢复运行System.out.println("准备完毕,开始!");}
}
看下运行结果:
两位选手请准备!
一号准备完毕,用时1秒!
二号准备完毕,用时2秒!
准备完毕,开始!
其实看到这里大家应该都懂了,没啥可说的,太简单了,接下来一起简单分析下源码,了解下它是如何实现的吧
CountDownLatch是基于AQS的同步器
* Constructs a {@code CountDownLatch} initialized with the given count.** @param count the number of times {@link #countDown} must be invoked* before threads can pass through {@link #await}* @throws IllegalArgumentException if {@code count} is negative*/
public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}
看下CountDownLatch的构造函数,就是传入一个int数值,这个数值就是状态变量,点击Sync()发现是一个setState的设置状态的函数,再点进去发现进去的是AQS的一个方法,count就是AQS内部的一个volatile变量
/*** The synchronization state.*/
private volatile int state;
volatile代表着啥我就不用多说了吧,还不清楚的传送门在这里学习volatile这一篇就够了,保证了可见性,有序性
我们看下内部的await()和counDown()的实现方法:
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}public boolean await(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}public void countDown() {sync.releaseShared(1);
}
发现内部的实现都是通过sync实现的,这个sync对象时何方神圣呢?\
/*** Synchronization control For CountDownLatch.* Uses AQS state to represent count.*/
private static final class Sync extends AbstractQueuedSynchronizer { }
哦,原来如此,是AQS的实现类啊原来,也就是CountDownLatch内部的主要方法是通过AQS来实现的
前面的我是看懂了,但是这个AQS到底是个什么东西呢?
AQS是一个多线程访问共享资源的同步器框架,资源共享的方式有两种,即独占Exclusive和共享Share;独占即只有一个线程能够执行,控制并发安全,例如ReentrantLock,共享即多个线程可以同时执行,比如我们现在说的CountDownLatch
内部通过维护了一个state共享资源和一个FIFO线程等待队列来实现的,自定义同步器时只需要实现共享资源state的获取和释放的方式即可。
CyclicBarrier
上面说到了一次性的锁,有时我们可能需要重复设置这个锁,这个场景如何满足呢?CyclicBarrier可以满足这个场景,这个工具类是可以重复使用的,通过reset来设置即可
和CountDownLatch不同的是,CyclicBarrier在指定数量的线程到达之前必须互相等待,也是因为在等待的线程被释放之后可以重复使用
在网上看到的一种说法叫做人满发车,很合适,车没有满的时候车上乘客需要等待,车到达目的地之后再返回出发点,重新等待发车
看个例子:
public class TestCyclicBarrier {public static void main(String[] args) {//初始化四个线程int threadNum = 4;CyclicBarrier barrier = new CyclicBarrier(threadNum, new MyThread());for (int i = 0; i < threadNum; i++) {new TestThread(barrier).start();}}static class TestThread extends Thread {private CyclicBarrier cyclicBarrier;public TestThread(CyclicBarrier cyclicBarrier) {this.cyclicBarrier = cyclicBarrier;}@Overridepublic void run() {System.out.println("线程" + Thread.currentThread().getName() + "正在执行");try {Thread.sleep(3000); //以睡眠来模拟操作System.out.println("线程" + Thread.currentThread().getName() + "执行完毕,等待其他线程执行完成");cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();}System.out.println("所有线程执行完成,继续处理其他任务...");}}static class MyThread extends Thread {@Overridepublic void run() {System.err.println("我是特殊任务");}}
}
输出结果如下:
线程Thread-3正在执行
线程Thread-2正在执行
线程Thread-4正在执行
线程Thread-1正在执行
线程Thread-1执行完毕,等待其他线程执行完成
线程Thread-2执行完毕,等待其他线程执行完成
线程Thread-3执行完毕,等待其他线程执行完成
线程Thread-4执行完毕,等待其他线程执行完成
所有线程执行完成,继续处理其他任务...
所有线程执行完成,继续处理其他任务...
所有线程执行完成,继续处理其他任务...
所有线程执行完成,继续处理其他任务...
我是特殊任务
CyclicBarrier内部是通过一个ReentrantLock的锁对象来控制的,基于Condition条件队列来对线程进行阻塞
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
内部也是一个计数器,每当线程到达屏障点的时候调用await()将自己阻塞,计数器减一,当计数器变为0的时候所有因为调用await()方法阻塞的线程都会被唤醒\
CyclicBarrier和CountDownLatch的不同点:
1、CountDownLatch是调用await的线程等待,而CyclicBarrier是大家一起等,互相等待;
2、CountDownLatch是一次性的,而CyclicBarrier是可以重复使用的;
在一定程度上,CyclicBarrier和CountDownLatch是相似的,在一些场景下两者均可实现,比如当多个线程共同达到同一条件,一起继续执行这种,两者均可实现
Semaphore
我们在商场找停车位,车位一般是固定的,车位属于共享资源,更多的车子可能会对这些固定数量的车位进行“抢夺”,这种情景好像上面两个工具类都不太好解决,JDK中提供了信号量Semaphore这个工具类,用于在多线程环境下能够协调各个线程对共享资源的正确、合理使用。
初始化的时候需要为这个许可集传入一个数值,这个数值代表同一时刻能够访问共享资源的线程数量,线程通过acquire()获得一个许可,然后对共享资源进行操作,如果许可集分配完了,线程进入等待状态,知道其他线程释放许可才有机会获得许可。
看一个例子:
public class TestSemaphore {public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);//3个停车位for (int i = 0; i <6 ; i++) {//模拟6部汽车new Thread(()->{try {semaphore.acquire();//占到车位System.out.println(Thread.currentThread().getName()+" 抢占到车位");TimeUnit.SECONDS.sleep(5);//模拟车停5秒System.out.println(Thread.currentThread().getName()+" 停车3秒后离开车位");} catch (InterruptedException e) {e.printStackTrace();}finally {semaphore.release();//释放停车位}},String.valueOf(i)).start();}}
}
输出结果是:\
2 抢占到车位
3 抢占到车位
1 抢占到车位
2 停车3秒后离开车位
3 停车3秒后离开车位
1 停车3秒后离开车位
0 抢占到车位
4 抢占到车位
5 抢占到车位
4 停车3秒后离开车位
0 停车3秒后离开车位
5 停车3秒后离开车位
即初始化停车位数量(许可集),每个车子代表一个线程,进入停车场会获得一个许可,占用共享资源。一旦停车位全部被占,未分配到车位的车子进入等待状态,等其它车子释放车位才有机会获得车位
Semaphore内部也是通过AQS实现的,当许可集的数量设置成1的时候可以来做一个互斥锁,感兴趣的同学自己去研究发觉
Exchanger
还有一个常见的工具类就是Exchanger,这个我们听名字估计就能猜出大概,但凡英语过个四级这个单词应该都认识
Exchanger,就是提供了两个线程互相交换数据的同步点,即一个线程完成一定的事务之后想要和另一个线程交换数据,则需要拿出数据,等待另一个线程的到来。
public class TestExchanger {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(new Runnable() {@Overridepublic void run() {String book = "《Java核心技术卷一》";try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("甲带着" + book + "到达交易地点!");try {System.out.println("甲换出了:" + book + ",换回了:" + exchanger.exchange(book));} catch (InterruptedException e) {e.printStackTrace();}}}).start();new Thread(new Runnable() {@Overridepublic void run() {String money = "200元";try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("乙带着" + money + "到达交易地点!");try {System.out.println("乙换出了:" + money + ",换回了:" + exchanger.exchange(money));} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}
输出结果:
甲带着《Java核心技术卷一》到达交易地点!
乙带着200元到达交易地点!
甲换出了:《Java核心技术卷一》,换回了:200元
乙换出了:200元,换回了:《Java核心技术卷一》
Exchanger(交换者)是一个用于线程间协作的工具类。
Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。
这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
因此使用Exchanger的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。
因此该工具类的线程对象是成对的
唠叨
船长希望有一天能够靠写作养活自己,现在还在磨练,这个时间可能会持续很久,但是,请看我漂亮的坚持
感谢大家能够做我最初的读者和传播者,请大家相信,只要你给我一份爱,我终究会还你们一页情的。
船长会持续更新技术文章,和生活中的暴躁文章,欢迎大家关注【Java贼船】,成为船长的学习小伙伴,和船长一起乘千里风、破万里浪
哦,对了!后续的更新文章我都会及时放到Java成神之路,欢迎大家点击观看,都是干货文章啊,建议收藏,以后随时翻阅查看
推荐阅读
● 小白读了这篇JVM,直呼真香,淦!(长篇干货预警)
● 一篇搞懂ElasticSearch(附学习脑图)
你应该知道的四种并发工具类相关推荐
- 一文搞懂四种同步工具类
作者:CoderV的进阶笔记 https://juejin.cn/post/6844903958360621064 CountDownLatch 解释: CountDownLatch相当于一个门闩, ...
- 第十章_多线程(2)_线程池原子性并发工具类
目录 一.线程池 1 - 线程状态 2 - 线程池 3 - Executors线程池 二.Volatile 三.原子性 四.并发工具类 1 - 并发工具类-Hashtable 2 - 并发工具类-Co ...
- 并发工具类(四)线程间的交换数据 Exchanger
前言 JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...
- 并发工具类(四)两个线程进行数据交换的Exchanger
简介 Exchanger(交换者)是一个用于线程间协作的工具类.Exchanger用于进行线程间的数据交换.它提供一个同步点,在这个同步点两个线程可以交换彼此的数据.这两个线程通过exchange方法 ...
- macbook oracle 工具,Navicat for Oracle Mac 四种实用工具
Navicat for Oracle Mac 凭借精心设计的用户界面,可以简便快捷地以安全且简单的方法创建.组织.访问和共享信息,优化 Oracle 管理.除了 Oracle 的核心功能外,Navic ...
- 【专家坐堂】四种并发编程模型简介
本文来自网易云社区 概述 并发往往和并行一起被提及,但是我们应该明确的是"并发"不等同于"并行" • 并发 :同一时间 对待 多件事情 (逻辑层面) ...
- 并发工具类(二)同步屏障CyclicBarrier
前言 JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...
- 三个好用的并发工具类
转载自 三个好用的并发工具类 以前的文章中,我们介绍了太多的底层原理技术以及新概念,本篇我们轻松点,了解下 Java 并发包下.基于这些底层原理的三个框架工具类. 它们分别是: 信号量 Semaph ...
- Java多线程系列(九):CountDownLatch、Semaphore等4大并发工具类详解
之前谈过高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 ,以及高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8) 今天主要介绍concurre ...
- 【重难点】【JUC 02】volitale 常用模式 、JUC 下有哪些内容 、并发工具类
[重难点][JUC 02]volitale 常用模式 .JUC 下有哪些内容 .并发工具类 文章目录 [重难点][JUC 02]volitale 常用模式 .JUC 下有哪些内容 .并发工具类 一.v ...
最新文章
- VTK:相互作用之ShiftAndControl
- Android 硬件 OpenGL ES 模拟设计概述
- CF1237F Balanced Domino Placements(dp+组合计数)
- Windows XP下Service的编程入门[1]
- 3D引擎多线程:渲染与逻辑分离
- IDEA配置SpringBoot的springloaded热部署(写方法、属性不用重启)
- 21天学通Java学习笔记-Day03
- java碳纤维折叠车评测_开启轻奢生活 SAVA Z1碳纤维折叠车 评测
- c语言报告收获,c语言学习心得体会(最新整理)
- 条码追溯系统解决外贸企业进销存管理
- 使用Mixamo绑定骨骼导入动画
- python豆瓣mysql_Python3.5爬取豆瓣电视剧数据并且同步到mysql中
- Vim 输入法在normal和insert之间自动切换
- IPTV的前世今生与发展
- 冥想五个程序r_冥想将使您成为一个更好的程序员:这就是方法。
- 为什么容器内存占用居高不下,频频 OOM(续)
- Linux下/sys目录介绍
- 期货的操作方法(期货的操作方法包括)
- 燃气爆炸竟然是这个四个原因?
- wkhtmltopdf-实现Html转pdf
热门文章
- 我看过的关于职业规划最好最全面的一篇文章
- 浏览器全球的书签都在这里了,看看有没有你的!
- 英语对于软件开发者来说到底有多重要?
- Mysql 按当天、当月、上月及按日期范围查询 DATE_FORMAT( date, ‘%Y%m‘ )
- 从辅助运动到让人开口说话,脑机接口:“你的福气还在后头!”
- paypal如何支付欧元_涨姿势!Paypal怎么用?
- 线性代数之——复数矩阵
- python-matplotlib绘图 -应用subplots_adjust()方法解决图表与画布的间距问题
- js获取url一级域名的方法
- 无人机+AI人工智能可以实现哪些领域的场景应用?