CountDownLatch介绍
CountDownLatch称之为闭锁,它可以使一个或一批线程在闭锁上等待,等到其他线程执行完相应操作后,闭锁打开,这些等待的线程才可以继续执行。确切的说,闭锁在内部维护了一个倒计数器。通过该计数器的值来决定闭锁的状态,从而决定是否允许等待的线程继续执行。
常用方法:
public CountDownLatch(int count):构造方法,count表示计数器的值,不能小于0,否者会报异常。
**public void await() throws InterruptedException:**调用await()会让当前线程等待,直到计数器为0的时候,方法才会返回,此方法会响应线程中断操作。
public boolean await(long timeout, TimeUnit unit) throws InterruptedException:限时等待,在超时之前,计数器变为了0,方法返回true,否者直到超时,返回false,此方法会响应线程中断操作。
**public void countDown():**让计数器减1
CountDownLatch使用步骤:
1.创建CountDownLatch对象
2.调用其实例方法 await(),让当前线程等待
3.调用 countDown()方法,让计数器减1
4.当计数器变为0的时候, await()方法会返回
举例:有3个人参见跑步比赛,需要先等指令员发指令枪后才能开跑,所有人都跑完之后,指令员喊一声,大家跑完了
package com.example.demo.demo.CountDownLatchDemo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 代码中,t1、t2、t3启动之后,都阻塞在 commanderCd.await();,
* 主线程模拟发枪准备操作耗时5秒,然后调用 commanderCd.countDown();
* 模拟发枪操作,此方法被调用以后,阻塞在 commanderCd.await();的3个线程会向下执行。
* 主线程调用 countDownLatch.await();之后进行等待,每个人跑完之后,调用 countDown.countDown();
* 通知一下 countDownLatch让计数器减1,最后3个人都跑完了,主线程从 countDownLatch.await();返回继续向下执行
*/
public class CountDownLatchTest {
public static class T extends Thread{
//每个线程跑步耗费的时间
int runCostTime;
CountDownLatch commanderCd;
CountDownLatch countDownLatch;
public T(String name, int runCostTime, CountDownLatch commanderCd,CountDownLatch countDownLatch) {
super(name);
this.runCostTime = runCostTime;
this.commanderCd = commanderCd;
this.countDownLatch=countDownLatch;
}
@Override
public void run() {
//准备跑步,等待发枪信号
try {
commanderCd.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 开始时间
long startTime = System.currentTimeMillis();
System.out.println(startTime+","+Thread.currentThread().getName()+"开始跑!");
try {
//跑步消耗的时间
TimeUnit.SECONDS.sleep(this.runCostTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
countDownLatch.countDown();
}
long endTime = System.currentTimeMillis();
System.out.println(endTime+","+Thread.currentThread().getName()+"跑步结束,耗时:"+(endTime-startTime));
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis()+","+Thread.currentThread()+"启动!");
CountDownLatch commanderCd = new CountDownLatch(1);
CountDownLatch countDownLatch = new CountDownLatch(3);
long startTime = System.currentTimeMillis();//比赛开始时间
T t1= new T("小张",2,commanderCd,countDownLatch);
t1.start();
T t2= new T("小红",5,commanderCd,countDownLatch);
t2.start();
T t3= new T("小黄",7,commanderCd,countDownLatch);
t3.start();
//线程在队列中等待,让主线程等待,准备发枪,其余线程进入就绪
TimeUnit.SECONDS.sleep(5);
System.out.println(System.currentTimeMillis()+",枪响了,开始跑");
commanderCd.countDown();//赛道上比赛开始跑起来
countDownLatch.await();
long endTime = System.currentTimeMillis();
System.out.println(System.currentTimeMillis()+","+Thread.currentThread().getName()+"结束,耗时"+(endTime-startTime));
}
}
1591104244695,Thread[main,5,main]启动!
1591104249699,枪响了,开始跑
1591104249699,小张开始跑!
1591104249699,小红开始跑!
1591104249699,小黄开始跑!
1591104251700,小张跑步结束,耗时:2001
1591104254699,小红跑步结束,耗时:5000
1591104256700,小黄跑步结束,耗时:7001
1591104256700,main结束,耗时12003
手写一个并行处理任务的工具类
package com.example.demo.demo.CountDownLatchDemo;
import org.springframework.util.CollectionUtils;
import java.sql.Time;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 并行处理任务的工具类
*/
public class TaskDisposeUtils {
//并发线程数
public static final int Pool_size;
static {
Pool_size = Integer.max(Runtime.getRuntime().availableProcessors(),5);
}
/**
* 并行性处理,等待结束
*/
public static <T> void dispose(List<T> taskList, Consumer<T> consumer) throws InterruptedException{
dispose(true,Pool_size,taskList,consumer);
}
/**
* 并行处理,并等待结束
* @param moreThread 是否多线程执行
* @param pool_size 线程池大小
* @param taskList 任务列表
* @param consumer 消费者
* @param <T>
*/
public static <T> void dispose(boolean moreThread, int pool_size, List<T> taskList, Consumer<T> consumer) throws InterruptedException {
if (CollectionUtils.isEmpty(taskList)){
return;
}
if(moreThread && pool_size>1){//多线程
pool_size = Math.min(pool_size,taskList.size());
ExecutorService executorService = null;
try{
executorService = Executors.newFixedThreadPool(pool_size);//创建线程池
CountDownLatch countDownLatch = new CountDownLatch(taskList.size());
for (T item:taskList) {
executorService.execute(() ->{
try {
consumer.accept(item);
}
finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
}
finally {
if(executorService !=null){
executorService.shutdown();
}
}
}else {//单线程
for (T item:taskList) {
consumer.accept(item);
}
}
}
public static void main(String[] args) throws InterruptedException {
//生成1-10的数字,放在list中,相当于10个任务
List<Integer> list = Stream.iterate(1, a -> a + 1).limit(10).collect(Collectors.toList());
//启动多线程处理list中的数据,每个任务休眠时间为list中的数据
TaskDisposeUtils.dispose(list,item->{
try{//实际开发,并行任务处理在try中
long startTime = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(item);
long endTime = System.currentTimeMillis();
System.out.println(System.currentTimeMillis()+".任务"+item+"执行完毕,耗时"+(endTime-startTime));
}catch (InterruptedException e){
e.printStackTrace();
}
});
System.out.println(list+"中的任务处理完毕");
}
}
1591106690601.任务1执行完毕,耗时1000
1591106691602.任务2执行完毕,耗时2001
1591106692601.任务3执行完毕,耗时3000
1591106693601.任务4执行完毕,耗时4000
1591106694602.任务5执行完毕,耗时5000
1591106695603.任务6执行完毕,耗时6001
1591106696602.任务7执行完毕,耗时7000
1591106697602.任务8执行完毕,耗时8000
1591106698602.任务9执行完毕,耗时9000
1591106699602.任务10执行完毕,耗时10000
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中的任务处理完毕