前言

苏宁估计是今年最大的黑马了,我们在开始中没有期望能战胜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()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的

结束语

感谢大家能够做我最初的读者和传播者,请大家相信,只要你给我一

份爱,我终究会还你们一页情的。

欢迎大家关注我的公众号【左耳君】,探索技术,分享生活

哦对了,后续所有的文章都会更新到这里

https://github.com/DayuMM2021/Java

八仙过海,四种同步(Java中的四种同步类)相关推荐

  1. java中有几种内部类,Java中的四种内部类

    四种内部类 在Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的 内部类一般来说包括这四种:静态内部类.匿名内部类.成员内部类和局部内部类,下面我们一一介绍 ...

  2. java中的四种代码块

    原文链接: java中的四种代码块_Munt的博客-CSDN博客_java中代码块 在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造 ...

  3. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  4. Java 中的四种引用

    垃圾收集器与内存分配策略参考目录: 1.判断Java 对象实例是否死亡 2. Java 中的四种引用 3.垃圾收集算法 4. Java9中的GC 调优 5.内存分配与回收策略 在进行垃圾回收之前,虚拟 ...

  5. Java中的四个核心技术思想

    Java中的四个核心技术思想 对Java核心概念和思想的掌握有助于提升我们对整个Java平台的理解力.这里将介绍四个Java中的核心技术思想,包括Java虚拟机.类装载器的体系结构.class文件和A ...

  6. java中的7种单例模式

    java中的7种单例模式 单例模式是我们开发中经常会用到的,单例模式的好处是应用在运行时只有一个实例,生命周期从单例实例化后一直到应用生命周期结束.这里总结和比较一下几种单例的形式,共总结了七种. 写 ...

  7. java中的几种锁(很详细)-小白收藏

    最近学习java中的几种锁,看到比较详细的一篇,先转发,后续补充自己的见解 其实如果按照名称来说,锁大概有以下名词:  自旋锁 ,自旋锁的其他种类,阻塞锁,可重入锁 ,读写锁 ,互斥锁 ,悲观锁 ,乐 ...

  8. java中的复合数据类型是什么_【填空题】类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素。一个类的实现包括两部分:____和_____....

    [填空题]类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素.一个类的实现包括两部分:____和_____. 更多相关问题 [名词解释] 观叶树木 [单选] 开花时有浓郁香气的树种是 ...

  9. 分析Java中的三种不同变量的区别

    1.首先分析Java中的三种不同变量的区别,如下表所示   概念 默认值 其他 类变量 也叫静态变量,是类中独立于方法之外的变量 用static 修饰 有默认初始值,系统自动初始化. 如boolean ...

  10. java中的五种排序方法_用Java排序的五种有用方法

    java中的五种排序方法 Java排序快速概述: 正常的列表: private static List VEGETABLES = Arrays.asList("apple", &q ...

最新文章

  1. Vicious Keyboard CodeForces - 801A (暴力+模拟)
  2. hibernate 集合类(Collections)映射
  3. eclipse 的习惯配置
  4. sublime text 3170 破解工具
  5. 公司mysql部署文档_Mysql部署文档
  6. python源码分析工具_python 域名分析工具实现代码
  7. 关于数组的 slice() 和 splice() 方法
  8. 表按某个字段,新增一个序号列
  9. 使用JQuery做一组复选框的功能。
  10. c语言 归一化图片大小,OpenCV学习笔记(1)——resize函数实现图像大小归一化
  11. Machine Learning - week 4 - Non-linear Hypotheses
  12. 汇新云为何给出严格的入驻审核标准?
  13. python pygame 游戏实战:Maze 迷宫生成,显示和游戏(附全部代码)
  14. css 压缩后出现了问题,觉得js或css压缩后不好调试?你应该认识一下source map了...
  15. GitHub的注册与使用
  16. 百位LOL英雄联盟角色合集
  17. ThingWorx中的Date操作
  18. 半监督分类算法简述,self-trainning,co-trainning
  19. 基于 STM32 和 NB-IoT 的可穿戴式老人防摔监测系统
  20. 数据库原理与技术 作业及答案 复习用

热门文章

  1. 迅捷校园网自动登录脚本(网页端)
  2. ###【Python版本】股票行情API:获取A股主流指数成分股st股和次新股日内资金净流入A股个股实时盘口/历史行情数据基本财务数据/现金流量数据央行货币供应数据融资融券历史数据的Api
  3. 我想成为一个记计算机程序员英语作文,关于程序员的英语作文
  4. setlocale()函数的作用
  5. android电视与苹果手机图片,小米电视怎么投屏?图文讲解安卓和苹果手机投屏到小米电视方法...
  6. Win10如何开启CPU虚拟化
  7. Win10Edge护眼色设置
  8. 什么是IDS和IPS
  9. 【Git】clone项目push项目没反应,Cloning into...没下载
  10. 探索性因子分析和验证性因子分析有什么区别?