JUC包下的常用工具类

  • 1. CountDownLatch-闭锁
  • 2. CyclicBarrier-循环栅栏
  • 3. Semaphore-信号量
  • 4. Exchanger-线程数据交换器

这篇文章主要是关于 java.util.concurrent(JUC) 类包下的常用类

JUC是JDK5才引入的并发类库. JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是 CountDownLatch,CyclicBarrier,Semaphore和 Exchanger 下面分别进行简单的介绍.

1. CountDownLatch-闭锁

CountDownLatch类位于java.util.concurrent包下,利⽤用它可以实现类似计数器的功能.

CountDownLatch 的主要作用是利用计数来保证线程的执行顺序,有点像倒计时,当计数为0时某个线程才能开始执行

CountDownLatch类只提供了⼀一个构造器:

public CountDownLatch(int count) { }; //参数count为计数值

CountDownLatch中的常用方法,包括:

  • CountDownLatch(int count) : 构造方法,需要传入计数的初始值
  • void await() :等待线程调⽤用await()方法的线程会被一直被阻塞,它会等待直到count计数器的值为0才继续执行
  • boolean await(long timeout, TimeUnit unit) : 同上,但是加入了超时参数,如果超时了计数还不为0,也会照样执行,避免了一直阻塞
  • void countDown() : 计数减一
  • 闭锁 : 每个 CountDowmLatch对象的计数器在值减为0时不可恢复原值

使用场景

比如有一个任务A,它要等待其他3个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能.

模拟三个运动员跑步的场景

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;/*** @Author: Mr.Q* @Date: 2019-08-12 13:56* @Description:CountDownLatch阻塞线程,等到子线程减到相应count主线程才继续执行*/
class CountDownTest implements Runnable {private CountDownLatch countDownLatch;public CountDownTest(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() +"已经到达终点");countDownLatch.countDown();}
}public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {// 主线程等待三个子线程减为 0再恢复执行//参数count为计数值CountDownLatch countDownLatch = new CountDownLatch(3); System.out.println("Game start...");new Thread(new CountDownTest(countDownLatch),"Runner A").start();TimeUnit.SECONDS.sleep(1);new Thread(new CountDownTest(countDownLatch),"Runner B").start();TimeUnit.SECONDS.sleep(1);new Thread(new CountDownTest(countDownLatch),"Runner C").start();// 主线程阻塞,等待 Runner A,B,C都到达终点后再执行countDownLatch.await();System.out.println("All Runners have reached destination.\nGame end!");}
}

运行结果:

如果在输出 All Runners have reached destination.\nGame end!之前不调用 await()

则运行结果会出现:

此时主线程和A,B,C 三个线程并行,没有调用await()的主线程将不再阻塞.

2. CyclicBarrier-循环栅栏

循环栅栏

  • 循环:每个CyclicBarrier的对象可以重复使用
  • 栅栏 : 每个子线程都阻塞,让一组线程同时到达某个时间节点

使用场景

例如三个子线程 A,B,C 在分别写入数据,其中 A线程写完数据后会阻塞,等待 B,C线程也写完数据后,才恢复执行;此时主线程才能读数据

CyclicBarrier提供2个构造器器:

  • public CyclicBarrier(int parties) {}

所有子线程调用await()后,将计数器值减1并进入阻塞状态;
直到计数器值减为0时,所有调用await()阻塞的子线程再同时恢复执行

  • public CyclicBarrier(int parties, Runnable barrierAction) {}

所有调用await()阻塞的子线程在计数器值减为0后,随机挑选一个线程执行 barrierAction 任务后,所有子线程恢复执行

CyclicBarrier中常用的方法:

int await() : 挂起当前线程,直到所有线程组中的线程都完成后继续执行,返回当前线程到达次序

int await(long timeout, TimeUnit unit) : 加了一个超时参数

我们来拿做饭举个例子,众所周知,在脱单这一环节,会做饭的蓝人,在广大只会敲代码的搬砖工面前是有绝对优势的,有空了我一定要习得一手好厨艺…

大葱牛肉饺子做法

以做饺子为例,A,B,C三道工序

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;/*** @Author: Mr.Q* @Date: 2019-08-12 15:02* @Description:所有调用await()阻塞的子线程在计数器值减为 0后* 随机挑选一个线程执行 barrierAction任务后,所有子线程恢复执行*/
class CyclicActionTest implements Runnable {private CyclicBarrier cyclicBarrier;public CyclicActionTest(CyclicBarrier cyclicBarrier) {this.cyclicBarrier = cyclicBarrier;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "做饺子中...");try {TimeUnit.SECONDS.sleep(3);// 所有子线程再次都阻塞cyclicBarrier.await();System.out.println("所有操作都已完成!\t开吃...");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}
}
public class CyclicBarrierActionTest {public static void main(String[] args) throws InterruptedException {// 传入线程组的数量和当线程达到时间节点后要做的操作CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {@Overridepublic void run() {System.out.println("\n哪个环节最重要?: "+Thread.currentThread().getName()+"\n");}});new Thread(new CyclicTest(cyclicBarrier),"A 和面中,勿扰 ").start();TimeUnit.SECONDS.sleep(1);new Thread(new CyclicTest(cyclicBarrier),"B 剁饺子馅,包饺子").start();TimeUnit.SECONDS.sleep(1);new Thread(new CyclicTest(cyclicBarrier),"C 下锅煮饺子").start();}
}

待 A,B,C分别完成任务后,饺子才可以吃.

执行内容好像忘记改了,但是步骤是一样的

执行过程分析:

  1. A和完面之后便阻塞
  2. 此后B剁饺子馅,包饺子,完成之后也阻塞
  3. A,B此时在阻塞中,C下锅煮饺子
  4. 调用await()阻塞的子线程在计数器值减为0后(即A,B,C执行完之后,随机挑选一个线程执行 barrierAction任务(new Runnable(),哪个环节最重要?)
  5. 然后所有子线程恢复执行

3. Semaphore-信号量

Semaphore用于控制信号量的个数,构造时传入个数. 总数就是控制并发的数量.

控制并发的数量假如是5,程序执行前用acquire()方法获得信号,则可用信号变为4,程序执行完通过release()方法归还信号量,可用信号又变为5.

如果可用信号为0,acquire就会造成阻塞,等待release释放信号. acquire()release()可以不在同一个线程使用.


Semaphore实现的功能就类似厕所有5个坑位,假如有10个人要上厕所,那么同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了.

另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项. 单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合.


Semaphore类提供了2个构造器:

  • 参数permits表示许可数目,即同时可以允许多少线程进行访问
  • 多了⼀个参数fair表示是否是公平的,即等待时间越久的越先获取许可

Semaphore类中常用的方法

  • acquire() : 用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可
  • 获取一个许可获取permits个许可
  • release() : 用来释放许可;(在释放许可之前,必须先获得许可)
  • 释放一个许可

    释放permits个许可

8说了,网吧开黑走起!!!

import java.util.concurrent.Semaphore;/*** @Author: Mr.Q* @Date: 2019-08-12 16:25* @Description:Semaphore*/// 网吧开黑
class SemaphoreDemo implements Runnable {private Semaphore semaphore;private int num;public SemaphoreDemo(Semaphore semaphore, int num) {this.semaphore = semaphore;this.num = num;}@Overridepublic void run() {//尝试申请设备try {semaphore.acquire();System.out.println("玩家"+ this.num +"使用一台电脑打游戏...");Thread.sleep(2000);System.out.println("玩家"+ this.num +"释放一台设备!");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}
}
public class SemaphoreTest {public static void main(String[] args) {//信号量为5Semaphore semaphore = new Semaphore(5); //5台设备for(int i = 0; i < 8; i++) { //8名玩家new Thread(new SemaphoreDemo(semaphore,(i+1))).start();}}
}

可以看到这一组线程是同步的去执行的

4. Exchanger-线程数据交换器

使用场景

用于两个线程的数据交换(不能用于多个)

调用exchange()方法会阻塞当前线程,必须等到另外一个线程时才可进行数据交换

期待在最美的年华,遇到最美的你…

import java.util.concurrent.Exchanger;/*** @Author: Mr.Q* @Date: 2019-08-12 16:08* @Description:*/
public class ExchangerTest {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();Thread boyThread = new Thread(new Runnable() {@Overridepublic void run() {try {String boy = exchanger.exchange("我明白你会来,所以我等!\n");System.out.println("The boy said : To the most beautiful you ↓ \n" + boy);} catch (InterruptedException e) {e.printStackTrace();}}});boyThread.start();Thread girlThread = new Thread(new Runnable() {@Overridepublic void run() {try {String girl = exchanger.exchange("一个人心头上的微风,吹到另外一个人生活里去时,是偶然还是必然?\n" +"人生的理想,是情感的节制恰到好处,还是情感的放肆无边无涯?\n" +"生命的取与,是昨天的好,当前的好,还是明天的好?");System.out.println("\nThe girl said : To the world who knows me the most ↓ \n" + girl);} catch (InterruptedException e) {e.printStackTrace();}}});girlThread.start();}
}

boy 和 girl 说的话交换…

文章部分内容参考自:

  • JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore

  • Java多线程—JUC包下的常见类

  • boy 和 girl said 来自 沈从文

多线程十 JUC包下的常用工具类相关推荐

  1. Concurrent包下的常用并发类和普通类之间的区别

    1. ConcurrentHashMap和HashMap以及Hashtable之间的区别 1.1 HashMap不是线程安全的,key和value都可为null:而Hashtable是线程安全的,代码 ...

  2. Java常用工具类之异常、包装类、字符串处理类、集合框架实现类、输入输出流、多线程

    集合.多线程和I/O流等 介绍6种常用工具类: 1.如何应用异常处理程序中的问题?2.如何通过包装器类实现基本数据类型的对象化处理?3.字符串处理类String.StringBuilder是如何进行字 ...

  3. (转)JAVA 十六个常用工具类

    (转)JAVA 十六个常用工具类 一. org.apache.commons.io.IOUtils closeQuietly 关闭一个IO流.socket.或者selector且不抛出异常.通常放在f ...

  4. 6.juc包下的原子类AtomicInteger,AtomicLong等AtomicXXX介绍

     在介绍juc中的原子类之前,先看看官方文档对java.util.concurrent.atomic包的介绍官方文档地址这里截取翻译之后的部分描述 1. 支持对单个变量进行无锁线程安全编程 2. 类的 ...

  5. JAVA常用工具类(实用高效)

    JAVA常用工具类(根据GITHUB代码统计) 从Google你能搜索到大量的关于Struts,Spring,Hibernate,iBatis等比较大的框架的资料,但是很少有人去关注一些小的工具包,但 ...

  6. javascript 总结(常用工具类的封装)(转)

    转载地址:http://dzblog.cn/article/5a6f48afad4db304be1e7a5f javascript 总结(常用工具类的封装) JavaScript 1. type 类型 ...

  7. javascript常用工具类整理(copy)

    JavaScript常用工具类 类型 日期 数组 字符串 数字 网络请求 节点 存储 其他 1.类型 isString (o) { //是否字符串return Object.prototype.toS ...

  8. Java 常用工具类整理

    目录 第一部分:常用的16个工具类 第二部分:java开发常用工具类(正则校验) 第一部分:常用的16个工具类 一.org.apache.commons.io.IOUtils 1.closeQuiet ...

  9. java escape工具类_java开发常用工具类

    在Java中,,工具类定义了一组公共方法.你把你的类继承这些类或者实现这些接口,就可以使用这些类的方法了.下面给大家介绍一下十六种最常用的java开发常用工具类. 一. org.apache.comm ...

最新文章

  1. Magento模块解析
  2. AfxBeginThread的介绍/基本用法和Window多线程使用详解
  3. asp.net在线预览txt文件(简单实现)
  4. linux 暂停一段时间,sleep命令_Linux sleep命令:让程序暂停或休眠一段时间
  5. 日志分析系统分类有哪些_Java开发日志规范
  6. 学习笔记(10):Python网络编程并发编程-粘包现象
  7. msdn中C#中常用词汇概念(转帖)
  8. 《软件项目管理(第二版)》第 10 章——项目收尾 重点部分总结
  9. 谈谈python的from __future__ import absolute_import
  10. 微信抢红包的方案_微信社群运营应该怎么运作?
  11. C++串口交互数据监听方法与虚拟串口工具安装
  12. 萌新分享打印文件夹下所有文件的代码
  13. 数论基础及其代码实现
  14. python opencv车辆测速视频汽车速度检测入侵检测测速
  15. 静态分析软件(QAC、Klocwork,Coverity等),单元测试软件集成测试软件 (VectorCAST、testbed、tessy、c++test等)下载安装使用试用
  16. 独创圆柱形投影,索尼高透光HOE全息显示方案详解
  17. 使用Java程序实现计算器
  18. 绿卡日记:2020-12-28
  19. Memcached命名空间
  20. 大数据之Hadoop3.x 运行环境搭建(手把手搭建集群)

热门文章

  1. echart 折线图的位置大小设置 通过grid{}属性实现
  2. 【PCB设计】晶振时钟电路布局设计
  3. 七夕到了,还在加班?98后小哥哥教你用 sula 快速配置页面提前下班去约会
  4. Halcon之多线程
  5. 神经网络matlab拟合,使用浅层神经网络拟合数据
  6. 干货 | 实现一个属于你的“语言”-携程Kotlin DSL开发与实践
  7. Google最强模型BERT出炉,NLP还有哪些值得期待的发展?
  8. 校园安全事故频发 安防建设从本质抓起
  9. H323plus的学习使用(1)——编译安装h323plus
  10. (已解决)多卡训练时报错RuntimeError: grad can be implicitly created only for scalar outputs