Fork me on GitHub

Java多线程系列——计数器 CountDownLatch

简介:

CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束

CountDownLatch 提供了两个主要的方法,await()、countDown()。

  • await:使当前线程阻塞,等待计数器为 0

  • countDown:计数器减一,计数为零时,释放所有在等待的线程

实例:

public class CountDownLatchDemo implements Runnable {
    static final CountDownLatch end = new CountDownLatch(10);
    static final CountDownLatchDemo demo = new CountDownLatchDemo();
    @Override
    public void run() {
        try {
            Thread.sleep(new Random().nextInt(10) * 1000);
            System.out.println("check complete...");
            end.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for (int i = 0;i < 10;i++) {
            exec.submit(demo);
        }
        end.await();
        System.out.println("Fire!");
        exec.shutdown();
    }
}

原理解析:

countDownLatch 的计数是通过一个共享变量(volatile)实现的,下面分析它的三个核心函数:构造函数,CountDownLatch(int count);阻塞线程,await();计数器减一,countDown()。

CountDownLatch(int count)

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

其中 Sync 是 CountDownLatch 的内部类,并且 Sync 继承了 AbstractQueuedSynchronizer

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;
    }
  }
}
View Code

其中 setState 是设置 AbstractQueuedSynchronizer 中 state 变量,该变量声明了 volatile。

State 就是 countDownLatch 中的计数器。

await()

public void await() throws InterruptedException {
  sync.acquireSharedInterruptibly(1);
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
      throws InterruptedException {
  if (Thread.interrupted())
    throw new InterruptedException();
  return tryAcquireShared(arg) >= 0 ||
    doAcquireSharedNanos(arg, nanosTimeout);
}

acquireSharedInterruptibly() 的作用是获取共享锁,如果当前线程处于中断状态,则抛出 InterruptedException,否则,调用 tryAcquireShared(arg) 尝试获取共享锁,如果锁计数器 = 0,则表示锁为可获取状态,返回 1,否则,锁为不可获取状态,则返回 -1。

doAcquireSharedNanos() 会使当前线程一直等待,直到当前线程获取到共享锁(或线程被中断)才返回。

countDown()

public void countDown() {
  sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
  if (tryReleaseShared(arg)) {
    doReleaseShared();
    return true;
  }
  return false;
}

releaseShared() 目的是让当前线程释放它所持有的共享锁。

tryReleaseShared() 的作用是释放共享锁,将锁计数器的值减一。

总结

CountDownLatch 是通过共享锁实现的。CountDownLatch 构造函数传递 int 参数,该参数是计数器的初始状态,表示共享锁最多能被 count 个线程同时获取。

当某线程调用 CountDownLatch 的 await 方法时,该线程会等待共享锁可用时(计数器为 0 时),才能获取共享锁,进而继续执行。

每次执行 countDown 时,会将计数器减一。

参考资料

Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

 
posted @ 2018-06-25 11:36  郑斌blog  阅读(2910)  评论(0编辑  收藏  举报