CountDownLatch就是一个同步的工具类,用来协调多个线程之间的同步或者是线程之间的通信。CountDownLatch有一个正数计数器,countDown()方法对计数器做减操作,await()方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。
闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:
- 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
- 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
- 等待直到某个操作所有参与者都准备就绪再继续执行。
CountDownLatch两种典型的用法:
- 某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
- 实现多个线程任务的最大并行性,注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的 CountDownLatch 对象,将其计数器初始化为 1 :new CountDownLatch(1),多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。
CountDownLatch的缺点:CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。
代码实例:
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo implements Callable<Integer> {
static int threadCount = 15;
static int awaitMillisecond = 3000;
static CountDownLatch countDownLatch = new CountDownLatch(threadCount);
int id;
public CountDownLatchDemo(int id) {
this.id = id;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
boolean fireFlag = true;
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(threadCount);
List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
for(int i = 0; i< threadCount; i++) {
Future<Integer> future = newFixedThreadPool.submit(new CountDownLatchDemo(i));
futureList.add(future);
}
for(int i = 0; i< threadCount; i++) {
Future<Integer> future = futureList.get(i);
if(0 == future.get()) {
fireFlag = false;
break;
}
}
if(true == fireFlag) {
System.out.println(getCurrentTime() + "go.");
}else {
System.out.println(getCurrentTime() + "countDownLatch.await().");
//设置超时时间awaitMillisecond
countDownLatch.await(awaitMillisecond, TimeUnit.MILLISECONDS);
System.out.println(getCurrentTime() + Thread.currentThread().getName() + ", 线程阻塞");
//线程阻塞
countDownLatch.await();
System.out.println(getCurrentTime() + "thread contains error, do not go.");
}
newFixedThreadPool.shutdown();
}
@Override
public Integer call() {
try {
if(id < 9) {
System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 检查完毕.");
countDownLatch.countDown();
return 1;
}else {
//int a = 1/0;
System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 存在异常,检查中...");
Thread.sleep(awaitMillisecond);
System.out.println(getCurrentTime() + Thread.currentThread().getName()+", 检查失败.");
return 0;
}
}catch (Exception e) {
System.out.println("call exception.");
return 0;
}
}
private static String getCurrentTime() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String format = sdf.format(date);
String ret = "[" + format + "] ";
return ret;
}
}
输出结果如下:
参考:https://blog.csdn.net/guorui_java/article/details/113827966