JDK1.8源码解读之CountDownLatch

585 阅读4分钟

前言

  • 一种同步帮助,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成为止。
  • {@code CountDownLatch}用给定的计数初始化。
  • {@link #await await}方法将阻塞,直到由于调用{@link #countDown}方法而导致当前计数达到零为止,此后所有等待线程都将被释放,{@link #await await}的所有后续调用将立即返回。
  • 这是一种一次性现象-无法重置计数。
  • 如果需要用于重置计数的版本,请考虑使用{@link CyclicBarrier}。
  • {@code CountDownLatch}是一种多功能的同步工具,可以用于多种用途。
  • 初始化为{@code CountDownLatch}的计数为一个简单的on /off锁存器或门:调用{@link #await await}的所有线程都在门处等待,直到被调用{@link#倒数}。
  • 初始化为N的{@code CountDownLatch}可用于使一个线程等待,直到N个线程完成某项操作或某项操作已完成N次。
  • {@code CountDownLatch}的一个有用属性是,它不需要调用{@code countDown}的线程在继续计数之前就等待计数达到零,它只是防止任何线程经过{@link #await await },直到所有线程都可以通过。
  • 用法示例:这是一对类,其中一组工作线程使用两个倒计时锁存器:第一个是启动信号,它防止任何工作程序继续进行,直到驱动程序为它们准备好为止。
  • 第二个是完成信号,驾驶员可以等待所有工人完成操作。
  • 另一个典型用法是将问题分为N个部分,用Runnable描述每个部分,该Runnable执行该部分并在锁存器上递减计数,然后将所有Runnable排队给执行程序。
  • 当所有子部分都完成时,协调线程将能够通过等待。
  • (当线程必须以此方式反复递减计数时,请使用{@link CyclicBarrier}。)

源码

package java.util.concurrent;
public class CountDownLatch {
    /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    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;
            }
        }
    }

    private final Sync sync;

    /**
     * 构造一个{@code CountDownLatch},并使用给定的计数初始化。
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    /**
     * 使当前线程等待,直到锁存器计数到零为止,除非该线程{@linkplain Thread#interrupt interrupted}。
     * 如果当前计数为零,则此方法立即返回。
     * 如果当前计数大于零,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下两种情况之一为止:
     * 由于调用{@link #countDown}方法而导致计数达到零;或某个其他线程{@linkplain Thread#interrupt interrupts}当前线程。
     * 如果当前线程:在进入此方法时设置了其中断状态;
     * 或在等待期间{@linkplain Thread#interrupt被中断},则抛出{@link InterruptedException}并清除当前线程的中断状态。
     */
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    /**
     * 使当前线程等待,直到锁存器计数到零为止,除非该线程{@linkplain Thread#interrupt interrupted},或者经过了指定的等待时间。
     * 如果当前计数为零,则此方法将立即返回值{@code true}。
     * 如果当前计数大于零,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一为止:由于调用{@link #countDown}方法而导致计数达到零;或其他某个线程{@linkplain Thread#interrupt interrupts}当前线程;或经过了指定的等待时间。
     * 如果计数达到零,则该方法返回值{@code true}。
     * 如果当前线程:在进入此方法时设置了其中断状态;或在等待期间{@linkplain Thread#interrupt被中断},则抛出{@link InterruptedException}并清除当前线程的中断状态。
     * 如果经过了指定的等待时间,则返回值{@code false}。
     * 如果时间小于或等于零,则该方法将根本不等待。
     */
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    /**
     * <p>If the current count equals zero then nothing happens.
     * 减少锁存器的计数,如果计数达到零,则释放所有等待线程。
     * 如果当前计数大于零,则将其递减。
     * 如果新计数为零,则将重新启用所有等待线程以进行线程调度。
     * 如果当前计数等于零,那么什么也不会发生。
     */
    public void countDown() {
        sync.releaseShared(1);
    }

    /**
     * 返回当前计数。此方法通常用于调试和测试目的。
     */
    public long getCount() {
        return sync.getCount();
    }

    /**
     * 返回标识此锁存器及其状态的字符串。括号中的状态包括字符串{@code“ Count =”},后跟当前计数。 
     */
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}