CountDownLatch原理

107 阅读2分钟

CountDownLatch

在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。 在CountDownLatch出现之前一般都使用线程的join()方法来实现这一点,但是join方法不够灵活,不能够满足不同场景的需要。

CountDownLatch使用案例:

public class JoinCountDownLatch {
  private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);

  public static void main(String[] args) throws InterruptedException {
      Thread t1 = new Thread(() -> {
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          } finally {
              countDownLatch.countDown();
          }
      });

      Thread t2 = new Thread(() -> {
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          } finally {
              countDownLatch.countDown();
          }
      });

      t1.start();
      t2.start();
      System.out.println("等待子线程执行完...");
      countDownLatch.await();
      System.out.println("子线程执行完毕");
  }
}

因为有两个子线程所以构造函数的传参为2。主线程调用countDownLatch.await()方法后会被阻塞。子线程执行完毕后调用countDownLatch.countDown()方法让countDownLatch内部的计数器减l,所有子线程执行完毕并调用countDown()方法后计数器会变为0,这时候主线程的await()方法才会返回。

实现原理

CountDownLatch使用AQS实现,构造函数:

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

可以发现,构造器实际上是把计数器的值赋给了AQS的状态变量state,也就是这里使用AQS的状态值来表示计数器值。

内部Sync

private static final class Sync extends AbstractQueuedSynchronizer

await()方法

当线程调用CountDownLatch对象的await方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回:当所有线程都调用了CountDownLatch对象的countDown方法后,也就是计数器的值为0时;其他线程调用了当前线程的interrupt()方法中断了当前线程,当前线程就会抛出InterruptedException异常,然后返回。

public void await() throws InterruptedException {
  sync.acquireSharedInterruptibly(1);
}

从以上代码可以看到,await()方法委托sync调用了AQS的acquireSharedInterruptibly方法,后者的代码如下:

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
      if (Thread.interrupted())
          throw new InterruptedException();
      if (tryAcquireShared(arg) < 0)
          doAcquireSharedInterruptibly(arg);
 }
 
 protected int tryAcquireShared(int acquires) {
      return (getState() == 0) ? 1 : -1;
  }

countDown()

线程调用该方法后,计数器的值递减,递减后如果计数器值为0则唤醒所有因调用await方法而被阻塞的线程,否则什么都不做。

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

 public final boolean releaseShared(int arg) {
      if (tryReleaseShared(arg)) {
          doReleaseShared();
          return true;
      }
      return false;
  }
  
 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;
      }
  }