Java并发编程(八)CountDownLatch

66 阅读1分钟

1. CountDownLatch的基本应用

  • CountDownLatch本身就好像一个计数器,可以让一个线程或多个线程等待其他线程完成后再执行

应用方式


public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
    // 声明CountDownLatch,有参构造传入的值,会赋值给state,CountDownLatch基于AQS实现
    CountDownLatch countDownLatch = new CountDownLatch(3);

    new Thread(() -> {
        System.out.println("111");
        // state -1
        countDownLatch.countDown();
    }).start();

    new Thread(() -> {
        System.out.println("222");
        // state -1
        countDownLatch.countDown();
    }).start();

    new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("333");
        // state -1
        countDownLatch.countDown();
    }).start();

    // 主线会阻塞在这个位置,直到CountDownLatch的state变为0
    countDownLatch.await();
    System.out.println("main");
}

2. CountDownLatch核心源码分析

2.1 从构造方法查看

// CountDownLatch 的有参构造
public CountDownLatch(int count) {
    // 健壮性校验
    if (count < 0) throw new IllegalArgumentException("count < 0");
    // 构建Sync给AQS的state赋值
    this.sync = new Sync(count);
}

2.2 countDown方法

// countDown方法,本质就是调用了AQS的释放共享锁操作
// 这里的功能都是AQS提供的,只有tryReleaseShared需要实现的类自己去编写业务
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        // 唤醒在AQS队列中排队的线程。
        doReleaseShared();
        return true;
    }
    return false;
}

// countDownLatch实现的业务
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0)
            return false;
        // state - 1
        int nextc = c-1;
        // 用CAS赋值
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
// 如果CountDownLatch中的state已经为0了,那么再次执行countDown跟没执行一样。
// 而且只要state变为0,await就不会阻塞线程。

2.3 await方法

// await方法
public void await() throws InterruptedException {
    // 调用了AQS提供的获取共享锁并且允许中断的方法
    sync.acquireSharedInterruptibly(1);
}

// AQS提欧的获取共享锁并且允许中断的方法
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // countDownLatch操作
    if (tryAcquireShared(arg) < 0)
        // 如果返回的是-1,代表state肯定大于0
        doAcquireSharedInterruptibly(arg);
}

// CountDownLatch实现的tryAcquireShared
protected int tryAcquireShared(int acquires) {
    // state为0,返回1,。否则返回-1
    return (getState() == 0) ? 1 : -1;
}

// 让当前线程进到AQS队列,排队去
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
    // 将当前线程封装为Node,并且添加到AQS的队列中
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                // 再次走上面的tryAcquireShared,如果返回的是的1,代表state为0
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    // 会将当前线程和后面所有排队的线程都唤醒。
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}