[JUC]CountDownLatch实现和原理

521 阅读2分钟

一、概述

CountDownLatch是限制某个线程等待其他线程全部执行后再执行; 相当于有一个计数器,每执行一次countDown方法计数器-1,知道计数器为0;在等待的线程便可以继续执行。 一般用于主线程需要使用多线程计算的结果

二、使用实例

private static AtomicInteger atomicInteger = new AtomicInteger();

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(100);
        for (int i = 0; i < 100; i++) {
            new Thread(new TestThread(countDownLatch)).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("result:" + atomicInteger.get());
    }

    private static class TestThread implements Runnable {

        private CountDownLatch countDownLatch;

        TestThread(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                System.out.println(atomicInteger.incrementAndGet());
            } finally {
                countDownLatch.countDown();
            }
        }
    }

主线程在等到所有线程都执行结束后打印结果:

三、源码解析

CountDownLatch同步器通过AQS的共享锁实现,使用state代表计数器

	private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
  1. 初始化给定state初始值
  2. 获取共享锁必须在state等于0才能获取成功
  3. 释放锁就是给计数器state减一

1、构造函数

CountDownLatch只提供了一个构造函数,必须指定不小于于0的计数器初始值

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

2、await()

await其实就是等待获取锁,成功则继续执行,失败则加入AQS队列自旋、休眠等待唤醒,直至成功获取锁;

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    // Sync中实现
	protected int tryAcquireShared(int acquires) {
         return (getState() == 0) ? 1 : -1;
     }

只有当state=0时才能获取锁成功,此方法是响应中断的,如果线程被中断则会抛出异常

3、await(long timeout, TimeUnit unit)

有超时时间的等待,如果在超时时间内没有获取锁,则立即返回false,线程继续

    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

此方法是响应中断的,如果线程被中断则会抛出异常;

4、countDown()

countDown其实就是释放共享锁

	public void countDown() {
        sync.releaseShared(1);
    }
	protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

如果state计数器已经等于0,则直接返回false,表示无需释放,不会唤醒队列中的线程;没有归零,则不断自旋+cas不断尝试,直至成功释放,或者计数器归零;当释放后计数器等于0,则会调用AQS中的方法唤醒等待线程。使用await方法等待的线程得到执行