J.U.C 系列之 Tools
JDK 5.0 开始,java并发大师Doug lea 为我们带来了更高效更应用的java并发API Java.util.concurrent包,简称J.U.C
J.U.C 体系由如下五个知识结构构成
本节我们首先来介绍其中的并发辅助工具类 Tools
Tools又由几个主要的工具类构成,如下所示‘
一 等待多线程完成的CountDownLatch
CountDownLatch允许一个或者多个线程等待其他线程完成操作。
假如有这样一个需求:我们需要解析一个Excel表里面的多个sheet数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,等到多有sheet的数据都解析完成之后,程序需要提示解析完成。在这个需求里面要求实现主线程等待所有线程完成sheet的解析操作,最简单的就是使用Thread 的join方法,这里不在讨论,若不清楚可以自行查看,我们这里来看看若使用CountDownLatch如何实现。
代码如下若是
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
/*
* CountDownLatch 构造函数接受一个int 类型的参数作为计数器。如果你想等待N个点完成,
这里就传入N.这里我们测试等待2个点完成
每次调用countDown方法N就会减一
*/
static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown();
System.out.println(2);
c.countDown();
}
}).start();
//await 方法会阻塞当前线程,直到计数器N的值变为0,
//由于countDown方法可以在任何线程执行,所以这里的N个点可以是N个线程
c.await();
System.out.println(3);
}
}
如果某个sheet的解析处理的异常缓慢,我们不可能让主线程一直等待,所以可以使用另一个带超时等待的await方法await(Long time,TimeUnit unit),这个方法等待指定时间后,就不会继续阻塞当前线程。
注:countDownLatch不能够重新初始化或者修改内部计数器值
二 同步循环屏障 CyclicBarrier
CyclicBarrier的字面量的意思是可循环使用的屏障。它主要做的是,让一组线程达到一个屏障时被阻塞,直到最后一个线程到达屏障点时,屏障才会被打开,所有被屏障拦截的线程才会继续执行。
具体效果图如下所示
CyclicBarrier 提供2个构造方法:
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时首先执行的任务。
下面我们通过实例演示如何使用CyclicBarrier
package com.wirt; import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { static CyclicBarrier c = new CyclicBarrier(2,new A()); public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(1); }
}).start(); try {
c.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} System.out.println(2);
} static class A implements Runnable{ @Override
public void run() {
System.out.println(3);
} }
}
因为设置了拦截数量是2,且两个线程都到达屏障后优先执行A任务,然后在执行到达屏障的任务
三 控制并发线程数的Semaphore
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,他通过协调各个线程以保证合理的使用公共资源。
Semaphore 可以用于做流量控制,特别是公共资源的应用场景,比如数据库连接,加入有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取。但是到读到内存后,还是要存储到数据库中,而数据库的连接数只有十个,这是我们必须控制只有十个线程同时获取数据库连接保存数据,否则报错无法获取数据库连接,这个时候,就可以使用Semaphore来做流量控制。示例代码如下
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreTest { private static final int COUNT = 30; private static ExecutorService threadPool = Executors.newFixedThreadPool(COUNT); private static Semaphore s = new Semaphore(10); public static void main(String[] args) {
for(int i =0;i<COUNT;i++){
threadPool.execute(new Runnable() { @Override
public void run() {
try {
s.acquire(); //获取许可证
System.out.println("save data");
s.release(); //释放许可证
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} threadPool.shutdown();
}
}
在代码中,虽然有30个线程任务在执行,但是只有10个线程可以同时并发执行。
四 线程间交换数据的Exchanger
Exchanger 是一个用于线程间协作的工具类,用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程通过执行exchanger方法,进行交换数据,如果第一个线程先执行exchanger,它会阻塞等待第二个线程执行exchanger方法,当两个线程都达到同步点时,这两个线程交换数据。
下面通过示例演示Exchanger用法
package com.wirt; import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class ExchangerTest {
private static final Exchanger<String> exgr = new Exchanger<String>(); private static final ExecutorService threadPool = Executors.newFixedThreadPool(2); public static void main(String[] args) {
threadPool.execute(new Runnable() { @Override
public void run() {
String A = "银行流水A"; //A录入银行流水数据
try {
exgr.exchange(A);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); threadPool.execute(new Runnable() { @Override
public void run() {
String B = "银行流水B"; //B录入银行流水数据
try {
String A = exgr.exchange(B);
System.out.println("A和B数据是否一致:"+A.equals(B)+"; A="+A+"; B="+B);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); threadPool.shutdown();
}
}
五 总结
| 工具类 | 使用场景 |
| CountDownLatch | 多线程同时解析一个Excel中多个sheet,等到所有sheet解析完成,程序提示完成 |
| CyclicBarrier | 多线程计算数据,最后合并计算结果 |
| Semaphore | 用于流量控制,特别是公共资源有限情况下的应用场景,例如数据库连接 |
| Exchanger | 1 可以用于遗传算法 2 可以用于校对工作 |
参考:《Java并发编程艺术》
工具类之Executors放在下一节
J.U.C 系列之 Tools的更多相关文章
- J.U.C 系列 Tools之Executors
上个章节说了Tools中的其他四个工具类,本节我们来看一看工具类中的老大Executors,为什么说它是老大,肯定是因为他的功能最多最强大. 一 Executors是什么 Executors 是一个线 ...
- j.u.c系列(11)---之并发工具类:Exchanger
写在前面 前面三篇博客分别介绍了CyclicBarrier.CountDownLatch.Semaphore,现在介绍并发工具类中的最后一个Exchange.Exchange是最简单的也是最复杂的,简 ...
- j.u.c系列(06)---之锁条件:Condition
写在前面 在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait().notify()系列方法可以实现等待/通知模式.在Java SE5后,Java提供了Lock接 ...
- j.u.c系列(03)---之AQS:AQS简介
写在前面 Java的内置锁一直都是备受争议的,在JDK 1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6后,进行大量的锁优化策略,但是与Lock相比synchron ...
- Day9 - J - 吉哥系列故事——恨7不成妻 HDU - 4507
单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 77=7 ...
- j.u.c系列(10)---之并发工具类:Semaphore
写在前面 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可:当信号量中有可用的许可 ...
- j.u.c系列(09)---之并发工具类:CyclicBarrier
写在前面 CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).因为该 barrier 在释放等待线程后可以重用,所以 ...
- j.u.c系列(08)---之并发工具类:CountDownLatch
写在前面 CountDownLatch所描述的是”在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待“:用给定的计数 初始化 CountDownLatch.由于调用了 countDo ...
- j.u.c系列(07)---之读写锁:ReentrantReadWriteLock
写在前面 重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少.然而读服务不存在数据竞争问题,如果一 ...
随机推荐
- C# 的两种debug 方法
第一种:需要把调试方法改成debug代码用 #if DEBUG 包裹 using System; using System.Collections.Generic; using System.Text ...
- 北航oo作业第二单元小结
类的设计: 首先,我对我的思路进行整体的说明,由于我的三次作业,思路是继承的,所以做总体的说明 第一, Main类,Main类自身并没有功能,他的功能只是构造需要的电梯线程和输入线程. 其中,第三 ...
- 《给业余投资者的10条军规 (雪球「岛」系列) (闲来一坐s话投资》读书笔记
大多数进入股市的人,往往有着非一般的自信.比如,读了几本大师的书,就感觉自己掌握了什么秘笈,又恰逢账面浮盈,自信心更是前所未有的膨胀. 有人说,投资者不经过一轮牛熊转换是成熟不起来的. 古人早就有言, ...
- 《Head First 设计模式》之命令模式——遥控器
命令模式(Command) ——将“请求”封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 要点 将发出请求的对象和执行请求的对象解耦. 被解耦的两者之间通过 ...
- addin修改启动路径
- Android 使用GreenDao 添加字段,删除表,新增表操作
GreenDao 给我个人感觉 比一般的ORM框架要好很多,虽然说上手和其他的比起来,较复杂,但是如果使用熟练以后,你会爱上这个框架的 用这些ORM 框架给我的感觉都是,当升级时,都需要进行数据库所有 ...
- C# 执行可执行文件
可以用C#脚本执行可执行文件,一般可以用C# IO流写出.bat脚本,然后顺带执行脚本,然后滑稽.三连... Process proc = null; try { proc = new Process ...
- python 学习之FAQ:find 与 find_all 使用
FAQ记录 1. 错误源码 错误源码如下 def fillUnivList(_html,_ulist): soup =BeautifulSoup(_html,'html.parser') fo ...
- mybatis-动态sql2
mybatis的动态sql中常用的有 if where foreach set 项目沿用之前的. 1.dao层添加接口: package com.java1234.map ...
- linux 命令——14 head (转)
head 与 tail 就像它的名字一样的浅显易懂,它是用来显示开头或结尾某个数量的文字区块,head 用来显示档案的开头至标准输出中,而 tail 想当然尔就是看档案的结尾. 1.命令格式: hea ...