​前言

苏宁估计是今年最大的黑马了,我们在开始中没有期望能战胜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(附学习脑图)

你应该知道的四种并发工具类相关推荐

  1. 一文搞懂四种同步工具类

     作者:CoderV的进阶笔记 https://juejin.cn/post/6844903958360621064 CountDownLatch 解释: CountDownLatch相当于一个门闩, ...

  2. 第十章_多线程(2)_线程池原子性并发工具类

    目录 一.线程池 1 - 线程状态 2 - 线程池 3 - Executors线程池 二.Volatile 三.原子性 四.并发工具类 1 - 并发工具类-Hashtable 2 - 并发工具类-Co ...

  3. 并发工具类(四)线程间的交换数据 Exchanger

    前言   JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...

  4. 并发工具类(四)两个线程进行数据交换的Exchanger

    简介 Exchanger(交换者)是一个用于线程间协作的工具类.Exchanger用于进行线程间的数据交换.它提供一个同步点,在这个同步点两个线程可以交换彼此的数据.这两个线程通过exchange方法 ...

  5. macbook oracle 工具,Navicat for Oracle Mac 四种实用工具

    Navicat for Oracle Mac 凭借精心设计的用户界面,可以简便快捷地以安全且简单的方法创建.组织.访问和共享信息,优化 Oracle 管理.除了 Oracle 的核心功能外,Navic ...

  6. 【专家坐堂】四种并发编程模型简介

    本文来自网易云社区 概述 并发往往和并行一起被提及,但是我们应该明确的是"并发"不等同于"并行" •       并发 :同一时间 对待 多件事情 (逻辑层面) ...

  7. 并发工具类(二)同步屏障CyclicBarrier

    前言   JDK中为了处理线程之间的同步问题,除了提供锁机制之外,还提供了几个非常有用的并发工具类:CountDownLatch.CyclicBarrier.Semphore.Exchanger.Ph ...

  8. 三个好用的并发工具类

    转载自  三个好用的并发工具类 以前的文章中,我们介绍了太多的底层原理技术以及新概念,本篇我们轻松点,了解下 Java 并发包下.基于这些底层原理的三个框架工具类. 它们分别是: 信号量 Semaph ...

  9. Java多线程系列(九):CountDownLatch、Semaphore等4大并发工具类详解

    之前谈过高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 ,以及高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8) 今天主要介绍concurre ...

  10. 【重难点】【JUC 02】volitale 常用模式 、JUC 下有哪些内容 、并发工具类

    [重难点][JUC 02]volitale 常用模式 .JUC 下有哪些内容 .并发工具类 文章目录 [重难点][JUC 02]volitale 常用模式 .JUC 下有哪些内容 .并发工具类 一.v ...

最新文章

  1. VTK:相互作用之ShiftAndControl
  2. Android 硬件 OpenGL ES 模拟设计概述
  3. CF1237F Balanced Domino Placements(dp+组合计数)
  4. Windows XP下Service的编程入门[1]
  5. 3D引擎多线程:渲染与逻辑分离
  6. IDEA配置SpringBoot的springloaded热部署(写方法、属性不用重启)
  7. 21天学通Java学习笔记-Day03
  8. java碳纤维折叠车评测_开启轻奢生活 SAVA Z1碳纤维折叠车 评测
  9. c语言报告收获,c语言学习心得体会(最新整理)
  10. 条码追溯系统解决外贸企业进销存管理
  11. 使用Mixamo绑定骨骼导入动画
  12. python豆瓣mysql_Python3.5爬取豆瓣电视剧数据并且同步到mysql中
  13. Vim 输入法在normal和insert之间自动切换
  14. IPTV的前世今生与发展
  15. 冥想五个程序r_冥想将使您成为一个更好的程序员:这就是方法。
  16. 为什么容器内存占用居高不下,频频 OOM(续)
  17. Linux下/sys目录介绍
  18. 期货的操作方法(期货的操作方法包括)
  19. 燃气爆炸竟然是这个四个原因?
  20. wkhtmltopdf-实现Html转pdf

热门文章

  1. 我看过的关于职业规划最好最全面的一篇文章
  2. 浏览器全球的书签都在这里了,看看有没有你的!
  3. 英语对于软件开发者来说到底有多重要?
  4. Mysql 按当天、当月、上月及按日期范围查询 DATE_FORMAT( date, ‘%Y%m‘ )
  5. 从辅助运动到让人开口说话,脑机接口:“你的福气还在后头!”
  6. paypal如何支付欧元_涨姿势!Paypal怎么用?
  7. 线性代数之——复数矩阵
  8. python-matplotlib绘图 -应用subplots_adjust()方法解决图表与画布的间距问题
  9. js获取url一级域名的方法
  10. 无人机+AI人工智能可以实现哪些领域的场景应用?