主要有两类
①并发流程控制相关:CountDownLatchCyclicBarrierSemaphore
②线程间交换数据相关:Exchanger


CountDownLatch

  • 作用:允许一个或多个线程等待其他线程完成操作
  • 使用步骤:
    ①定义一个CountDownLatch(称为计数器),并指定等待次数;
    ②在合适的时机将计数器减1;
    ③在需要等待所有任务结束的位置,调用await()方法;

根据JDK中的说明文档整理的两个例子:
例子1:

public class CountDownLatchLearning {

    public void doSomething() {

        CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10); //创建并启动线程
for (int i = 0; i < 10; ++i) {
new Thread(new Worker(startSignal, doneSignal)).start();
} doSomeThingBeforeAllThreadsProcess();
startSignal.countDown(); //让之前for循环创建的线程开始真正工作
try {
doneSignal.await(); // 等待之前for循环创建的线程执行结束
} catch (InterruptedException e) {
e.printStackTrace();
}
doSomeThingAfterAllThreadsProcess();
} private void doSomeThingAfterAllThreadsProcess() {
//所有任务开始前,做一些准备工作
} private void doSomeThingBeforeAllThreadsProcess() {
//所有任务开始后,做一些其他工作,如合并结果等等
} class Worker implements Runnable { private final CountDownLatch startSignal;
private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
} @Override
public void run() {
try {
startSignal.await();//等待,开始信号为0再继续向下进行
doWork();
} catch (InterruptedException ex) {
ex.printStackTrace();
}finally {
doneSignal.countDown();//完成后,将结束信号减1
}
} void doWork() {
//这里是真正有意义的任务
}
}
}

例子2:

public class CountDownLatchLearning1 {

    public void doSomething() {

        CountDownLatch doneSignal = new CountDownLatch(100);

        Executor e = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 100; ++i) {
e.execute(new WorkerRunnable(doneSignal, i));
} try {
doneSignal.await(); // 等待所有任务结束
} catch (InterruptedException e1) {
e1.printStackTrace();
}
} private void doSomeThingAfterAllThreadsProcess() {
//所有任务开始前,做一些准备工作
} private void doSomeThingBeforeAllThreadsProcess() {
//所有任务开始后,做一些其他工作,如合并结果等等
} class WorkerRunnable implements Runnable { private final CountDownLatch doneSignal;
private final int i; WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
} @Override
public void run() {
doWork(i);
doneSignal.countDown();
} void doWork(int i) {
//这里是真正的有意义的任务
}
}
}

CyclicBarrier

  • 作用:让一组线程等待至某个状态之后再全部同时执行,适用于多线程计算数据,最后合并计算结果的场景。
  • 使用:
①构造:
public CyclicBarrier(int parties, Runnable barrierAction) {}
public CyclicBarrier(int parties) {}
其中:
parties指让多少个线程或者任务等待至barrier状态;
barrierAction指当这些线程都达到barrier状态时会执行的内容; ②在合适的时机调用await方法,告诉CyclicBarrier我(当前线程)已经达到了屏障,然后当前线程被阻塞
public int await();
public int await(long timeout, TimeUnit unit);
返回当前线程到达屏障的次序( 0 ~ getParties() - 1) ③其他有用的方法
getNumberWaiting():获取CyclicBarrier阻塞的线程数量
isBroken():阻塞线程(一个或多个)是否被中断
reset():重置CyclicBarrier

举个例子:

public class CyclicBarriarExapmle {

    private Map<String, Integer> map = new ConcurrentHashMap<>();

    CyclicBarrier barrier = new CyclicBarrier(10, ()->{
//线程全部到达屏障后,执行的任务
System.out.println("我是线程全部到达屏障后,执行的任务");
int result = 0;
for(Map.Entry<String, Integer> entry : map.entrySet()){
result += entry.getValue();
}
System.out.println("最终计算结果:" + result);
}); private void calculate(){
for(int i = 0; i < 10; i++){
final int j = i;
new Thread(()->{
//执行计算,假如计算结果是,计算完成后,放入map中
System.out.println("当前计算结果:" + j);
map.put(Thread.currentThread().getName(), j);
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
} public static void main(String[] args){
CyclicBarriarExapmle exapmle = new CyclicBarriarExapmle();
exapmle.calculate();
}
}

程序最终输出:

当前计算结果:1
当前计算结果:4
当前计算结果:3
当前计算结果:2
当前计算结果:6
当前计算结果:0
当前计算结果:7
当前计算结果:5
当前计算结果:8
当前计算结果:9
我是线程全部到达屏障后,执行的任务
最终计算结果:45

CountDownLatch与CyclicBarrier的区别:二者都可以用来让一组线程等待其他线程,但CyclicBarrier功能更强大,可以重复使用,并可以设置优先任务。


Semaphore

  • 作用:控制同时访问特定资源的线程数量,进行流量控制
  • 使用:①创建Semaphore,根据资源特性,指定可以同时访问该资源的线程数量;②在具体使用资源的时候,首先从Semaphore获取许可证,使用完资源之后,释放资源
  • 值得注意的是:在一个线程release之前,并不一定要acquire。可以根据程序需要,自行控制。
/**
* Releases a permit, returning it to the semaphore.
*
* <p>Releases a permit, increasing the number of available permits by
* one. If any threads are trying to acquire a permit, then one is
* selected and given the permit that was just released. That thread
* is (re)enabled for thread scheduling purposes.
*
* <p>There is no requirement that a thread that releases a permit must
* have acquired that permit by calling {@link #acquire}.
* Correct usage of a semaphore is established by programming convention
* in the application.
*/
public void release() {
sync.releaseShared(1);
}

举个例子:

public class SemaphoreExample {
private Semaphore semaphore = new Semaphore(10);
private Executor executor = Executors.newFixedThreadPool(30);
public void calculate(){
for(int i = 0; i < 30; i++){
executor.execute(()->{
try {
//获取许可证
semaphore.acquire();
//执行计算
System.out.println("使用资源,执行任务");
//释放许可证
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}

Exchanger

  • 作用:线程间数据交换,它提供一个同步点,两个线程可以交换彼此的数据,这两个线程通过exchange()方法交换数据,如果第一个线程先执行该方法,它会一直等待第二个线程也执行该方法,当两个线程都到达同步点的时候,这两个线程就可以交换数据。
public class ExchangerExample {

    public static void main(String[] args){
Exchanger<String> exchanger = new Exchanger<>();
new Thread(()->{
String resultOne = "A";
try {
String exchangeResult = exchanger.exchange(resultOne);
System.out.println("我的计算结果是:" + resultOne + ",与我交换数据的那个线程计算的结果是:" + exchangeResult); } catch (InterruptedException e) {
e.printStackTrace();
}
}).start(); new Thread(()->{
String resultTwo = "B";
try {
String exchangeResult = exchanger.exchange(resultTwo);
System.out.println("我的计算结果是:" + resultTwo + ",与我交换数据的那个线程计算的结果是:" + exchangeResult);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}

参考

《Java并发编程的艺术》,有适当更改

20171219更新:增加对Semaphore的release方法的解释。

作者:maxwellyue
链接:https://www.jianshu.com/p/738d1ddd6731
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

【多线程与并发】Java并发工具类的更多相关文章

  1. Java Properties工具类详解

    1.Java Properties工具类位于java.util.Properties,该工具类的使用极其简单方便.首先该类是继承自 Hashtable<Object,Object> 这就奠 ...

  2. Java json工具类,jackson工具类,ObjectMapper工具类

    Java json工具类,jackson工具类,ObjectMapper工具类 >>>>>>>>>>>>>>> ...

  3. Java日期工具类,Java时间工具类,Java时间格式化

    Java日期工具类,Java时间工具类,Java时间格式化 >>>>>>>>>>>>>>>>>&g ...

  4. MinerUtil.java 爬虫工具类

    MinerUtil.java 爬虫工具类 package com.iteye.injavawetrust.miner; import java.io.File; import java.io.File ...

  5. MinerDB.java 数据库工具类

    MinerDB.java 数据库工具类 package com.iteye.injavawetrust.miner; import java.sql.Connection; import java.s ...

  6. 小记Java时间工具类

    小记Java时间工具类 废话不多说,这里主要记录以下几个工具 两个时间只差(Data) 获取时间的格式 格式化时间 返回String 两个时间只差(String) 获取两个时间之间的日期.月份.年份 ...

  7. Java Cookie工具类,Java CookieUtils 工具类,Java如何增加Cookie

    Java Cookie工具类,Java CookieUtils 工具类,Java如何增加Cookie >>>>>>>>>>>>& ...

  8. UrlUtils工具类,Java URL工具类,Java URL链接工具类

    UrlUtils工具类,Java URL工具类,Java URL链接工具类 >>>>>>>>>>>>>>>&g ...

  9. java日期工具类DateUtil-续一

    上篇文章中,我为大家分享了下DateUtil第一版源码,但就如同文章中所说,我发现了还存在不完善的地方,所以我又做了优化和扩展. 更新日志: 1.修正当字符串日期风格为MM-dd或yyyy-MM时,若 ...

  10. java日期工具类DateUtil-续二

    该版本是一次较大的升级,农历相比公历复杂太多(真佩服古人的智慧),虽然有规律,但涉及到的取舍.近似的感念太多,况且本身的概念就已经很多了,我在网上也是查阅了很多的资料,虽然找到一些计算的方法,但都有些 ...

随机推荐

  1. SQL 基本概念、通用语法与分类

    一.SQL 概念  1.什么是 SQL Structured  Query Language 结构化查询语句 2.SQL 作用 (1)是一种所有关系型数据库的查询规范,不同的数据库都支持. (2)通用 ...

  2. 【iOS录音与播放】实现利用音频队列,通过缓存进行对声音的采集与播放

    都说iOS最恶心的部分是流媒体,其中恶心的恶心之处更在即时语音. 所以我们先不谈即时语音,研究一下,iOS中声音采集与播放的实现. 要在iOS设备上实现录音和播放功能,苹果提供了简单的做法,那就是利用 ...

  3. 一些 SQLite技巧

    SQLite自增ID自段 使用方法为 INTEGER PRIMARY KEY AUTOINCREMENT 如: CREATE   TABLE  21andy ( id  INTEGER   PRIMA ...

  4. MySql时区修改

    1.查看当前时间 > select curtime(); #或select now()也可以+-----------+| curtime() |+-----------+| 15:18:10 | ...

  5. python(open文件读取)

    一.open文件读取 1.open('file','mode')打开一个文件 file 要打开的文件名,需加路径(除非是在当前目录) mode 文件打开的模式 需要手动关闭close 2.with o ...

  6. Linux命令——getconf

    转自:灵活使用getconf命令来获取系统信息 简介 getconf本身是个ELF可执行文件,用于获取系统信息 用法 getconf -a可以获取全部系统信息 对于这个命令,记住几个常用的信息获取方法 ...

  7. 【HICP Gauss】数据库 环境的搭建 -1

    1.安装规则    1.主机名必须网络唯一  2.主机名必须两位数以上 可以中划线 不能下划线  3.固定IP地址  4.端口号 1888 新增账户 omm 用户组 dbgrp ,家目录 /home/ ...

  8. openwrt配置strongswan对接hillstone ipsec的笔记

    一.主要参考资料: https://openwrt.org/docs/guide-user/services/vpn/ipsec/strongswan/roadwarrior https://open ...

  9. IP和网络互联

    IP和网络互联 IP网络互连机制: IP地址分类方法及原因: CIDR地址(无分类地址): IP分组首部格式: 数据分片方法: IP分组传输思路:

  10. Kotlin伴生对象及其字节码内幕详解

    继续面向对象,开撸就是!! 接口: 我们知道对于JDK8之后接口中除了方法的声明之后还可以有default方法的,而在Kotlin中也类似,下面来看一下在Kotlin接口相关的东东: 很显然就是一个方 ...