Java并发系列之五 CountDownLatch源码解析

时间:2022-06-05 17:19:50

CountDownLatch概述

引用一段CountDownLatch类注释

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

CountDownLatch是一个同步辅助类。它可以让一个或者多个线程一直阻塞直到其他线程完成了一些指定的操作。

下面我用一个实战案例来说明下CountDownLatch的使用。开启了5个线程去爬取网络上的数据。当爬取完N条数据时,爬取任务停止,让其他线程去汇总这N条数据。

public class CountDownLatchUsage {
    private final static int TASK_COUNT = 100;
    private volatile int count = 0;
    private CountDownLatch countDownLatch = new CountDownLatch(TASK_COUNT);
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                System.out.println("爬取数据中---");
                count++;
                countDownLatch.countDown();
                if (count == TASK_COUNT) break;
            }
        }
    };

    public static void main(String[] args) {
        final CountDownLatchUsage usage = new CountDownLatchUsage();
        ExecutorService service = Executors.newFixedThreadPool(5);
        service.submit(usage.runnable);
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    //这里加一个await()完美的解释了让多个线程阻塞
                    usage.countDownLatch.await();
                    System.out.println("子线程开始做任务");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        try {
            usage.countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("对数据做汇总 - " + usage.count);
    }
}

…….忽略 97条打印结果

获取到一条数据

获取到一条数据

获取到一条数据

子线程开始做任务

对数据做汇总 - 100

由结果可以看出 主线程和子线程都会等线程池打印完100条数据后打印

源码解析

CountDownLatch内部使用的是 共享锁。当线程调用CountDownLatch的await()。其实线程就是在获取共享锁。CountDownLatch(int count) 构造函数 count参数就是初始化了内部共享锁的Sync的state值。获取到共享锁的条件就是 state == 0。countDown()就是释放共享锁,主要是让state-1。如果执行了count次countDown()。state就等于0。在CountDownLatch上等待的线程将获取共享锁。

1. await()方法

public void await() throws InterruptedException {
        //获取共享锁
        sync.acquireSharedInterruptibly(1);
}

protected int tryAcquireShared(int acquires) {
        //当state==0的时候才能获取到锁
        return (getState() == 0) ? 1 : -1;
}

如果CountDownLatch(int count)构造函数 传入的count>0
只要线程调用了await()方法,那么该线程都会进入AQS的队列中等待。并且阻塞线程。那么只有当CountDownLatch调用了countDown()方法,并且state==0时,线程才能重新唤醒。

2. countDown()方法

public void countDown() {
    sync.releaseShared(1);
}

countDown()方法主要就是用来释放共享锁的。当然这里可能有的读者会有疑问。共享锁不是没有获取到吗,怎么还能释放呢。其实呢这里主要是将state-1了,并且唤醒AQS中等待的线程,自旋获取共享锁