Java多线程-22丨JUC-CountDownLatch(倒计时门闩)

Posted by jiefang on November 17, 2019

JUC-CountDownLatch

简介

CountDownLatch是一个同步辅助类,通过AQS实现的一个闭锁。在其他线程完成它们的操作之前,允许一个多个线程等待。简单来说,CountDownLatch中有一个锁计数,在计数到达0之前,线程会一直等待。

原理图

用法

1
2
3
4
5
6
7
8
9
10
11
    //初始化
    CountDownLatch countDownLatch = new CountDownLatch(10);
    for (int i = 0; i < 10; i++) {
        Executors.newFixedThreadPool(10).execute(()->{
            System.out.println("");
            //计数减1
            countDownLatch.countDown();
        });
    }
    //主线程阻塞
    countDownLatch.await();

原理

类图

CountDownLatch是一个“共享锁”,内部定义了自己的同步器SyncSync继承自AQS,实现了tryAcquireSharedtryReleaseShared两个方法。需要注意的是,CountDownLatch中的锁是响应中断的,如果线程在对锁进行操作期间发生中断,会直接抛出InterruptedException

image

源码

CountDownLatch.Sync源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        //构造函数,设置AQS的state
        Sync(int count) {
            setState(count);
        }
        //获取AQS的state
        int getCount() {
            return getState();
        }
        //AQS中tryAcquireShared的子类实现
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        //AQS中tryReleaseShared的子类实现
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                //该方法只有在count值原来不为0,但是调用后变为0时,才会返回true,否则返回false
                if (c == 0)
                    return false;
                int nextc = c-1;
                //state值从大于0变为0值时会返回true
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

CountDownLatch构造函数

1
2
3
4
5
6
7
8
9
    //CountDownLatch构造函数
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    //Sync构造函数,设置AQS state
    Sync(int count) {
        setState(count);
    }

await()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    //使当前线程等待,直到锁存器计数到零为止,除非该线程{@linkplain Thread#interrupt interrupted}。
    public void await() throws InterruptedException {
        //
        sync.acquireSharedInterruptibly(1);
    }
    //AQS中获取共享锁响应中断
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //交由子类实现
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

CountDownLatch()

1
2
3
4
5
6
7
8
9
10
11
12
    //减少锁存器的计数,如果计数达到零,则释放所有等待线程
    public void countDown() {
        sync.releaseShared(1);
    }
    //AQS释放共享锁
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }    

总结

  • CountDownLatch内部通过共享锁实现
  • 在创建CountDownLatch实例时,需要传递一个int型的参数:count,该参数为计数器的初始值,也可以理解为该共享锁可以获取的总次数。
  • 当某个线程调用await()方法,程序首先判断count的值是否为0,如果不会0的话则会一直等待直到为0为止。当其他线程调用countDown()方法时,则执行释放共享锁状态,使count - 1。
  • 当在创建CountDownLatch时初始化的count参数,必须要有count线程调用countDown()方法才会使计数器count等于0,锁才会释放,前面等待的线程才会继续运行。
  • CountDownLatch不能重置