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; } } }
其中 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 时,会将计数器减一。