简介
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。(如需重置,则使用CyclicBarrier)
模拟场景
给定100组初始化数据,根据某个规则对这100组数据进行处理,然后将结果记录到一个数组里面。然后进行排序。
解决方案
使用for循环,循环执行任务,将结果存在list里面,再排序
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<Integer> list = new ArrayList<Integer>();
System.out.println(sdf.format(new Date()));
for (int i = 0; i < 100; i++) {
list.add(doWork(i));
}
Collections.sort(list);
System.out.println(sdf.format(new Date()));
}
public static int doWork(Integer i) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
return (int) (Math.random() * 100);
}
执行时间:
2017-08-17 13:46:41
2017-08-17 13:47:01
由于是单线程,所以最后的时间是每次处理的时间之和
当使用countDownLatch的时候,代码:
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static List<Integer> res = new CopyOnWriteArrayList<Integer>();
private static final Integer LENGTH = 100;
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(LENGTH);
System.out.println("start at " + sdf.format(new Date()));
for (int i = 0; i < LENGTH; i++) {
Thread work = new Thread(new Worker(i, latch));
work.start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Collections.sort(res);
System.out.println("end at " + sdf.format(new Date()));
}
static class Worker implements Runnable {
int index;
CountDownLatch latch;
public Worker(int index, CountDownLatch latch) {
this.index = index;
this.latch = latch;
}
public void run() {
try {
Thread.sleep(200);
Integer i = new Random().nextInt(100);
res.add(i);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
}
执行时间:
start at 2017-08-17 14:41:37
end at 2017-08-17 14:41:38
由此可见,当我们使用countDownLatch的时候,相当于100个任务同时启动,然后先执行完的等待后执行完的结束后,我们再进行下一步的操作,大大的提高了效率。
(有个小细节:为什么我for循环的时候用的是ArrayList而countDownLatch用的是CopyOnWriteArrayList?线程安全)
注意的地方:
使用countDownLatch的时候,每一个任务执行完成之后,需要latch.countDown()来减少计数。因此我们需要对任务进行异常捕获,假如任务有问题,也需要调用latch.countDown(),否则就会造成await 方法会一直受阻塞而结束不了。