CountDownLatch

简介

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 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 list = new ArrayList();

        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 res = new CopyOnWriteArrayList();

    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 方法会一直受阻塞而结束不了。

你可能感兴趣的