CountDownLatch简介
CountDownLatch是通过“共享锁”实现的。在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count给线程同时获取。当某线程调用该CountDownLatch对象的await()方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行。而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!
CountDownLatch和CyclicBarrier的区别
(01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
(02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
CountDownLatch函数列表
CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。 // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 void await() // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 boolean await(long timeout, TimeUnit unit) // 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 void countDown() // 返回当前计数。 long getCount() // 返回标识此锁存器及其状态的字符串。 String toString()
学习CountDownLatch的核心函数
1.CountDownLatch(int count)
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
说明:该函数是创建一个Sync对象,而Sync是继承于AQS类。Sync构造函数如下:
Sync(int count) { setState(count); }
setState()在AQS中实现,源码如下:
protected final void setState(long newState) { state = newState; }
说明:在AQS中,state是一个private volatile long类型的对象。对于CountDownLatch而言,state表示的”锁计数器“。CountDownLatch中的getCount()最终是调用AQS中的getState(),返回的state对象,即”锁计数器“。
2.await()
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
说明:该函数实际上是调用的AQS的acquireSharedInterruptibly(1);
AQS中的acquireSharedInterruptibly()的源码如下:
public final void acquireSharedInterruptibly(long arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
说明:acquireSharedInterruptibly()的作用是获取共享锁。
如果当前线程是中断状态,则抛出异常InterruptedException。否则,调用tryAcquireShared(arg)尝试获取共享锁;尝试成功则返回,否则就调用doAcquireSharedInterruptibly()。doAcquireSharedInterruptibly()会使当前线程一直等待,直到当前线程获取到共享锁(或被中断)才返回。
tryAcquireShared()在CountDownLatch.java中被重写,它的源码如下:
protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
说明:tryAcquireShared()的作用是尝试获取共享锁。
如果"锁计数器=0",即锁是可获取状态,则返回1;否则,锁是不可获取状态,则返回-1。
3.countDown()
public void countDown() { sync.releaseShared(1); }
说明:该函数实际上调用releaseShared(1)释放共享锁。
releaseShared()在AQS中实现,源码如下:
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }
说明:releaseShared()的目的是让当前线程释放它所持有的共享锁。
它首先会通过tryReleaseShared()去尝试释放共享锁。尝试成功,则直接返回;尝试失败,则通过doReleaseShared()去释放共享锁。
tryReleaseShared()在CountDownLatch.java中被重写,源码如下:
protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { // 获取“锁计数器”的状态 int c = getState(); if (c == 0) return false; // “锁计数器”-1 int nextc = c-1; // 通过CAS函数进行赋值。 if (compareAndSetState(c, nextc)) return nextc == 0; } }
说明:tryReleaseShared()的作用是释放共享锁,将“锁计数器”的值-1。
CountDownLatch实例
public class testHello { private static int LATCH_SIZE = 5; private static CountDownLatch doneSignal; public static void main(String[] args) { try { doneSignal = new CountDownLatch(LATCH_SIZE); // 新建5个任务 for(int i=0; i<LATCH_SIZE; i++) new InnerThread().start(); System.out.println("main await begin."); // "主线程"等待线程池中5个任务的完成 doneSignal.await(); System.out.println("main await finished."); } catch (InterruptedException e) { e.printStackTrace(); } } static class InnerThread extends Thread{ public void run() { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " sleep 1000ms."); // 将CountDownLatch的数值减1 doneSignal.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
可以看出,主线程通过doneSignal.await()等待其他线程将doneSignal递减至0,其他的几个子线程,每次通过countDown函数将doneSignal值减1。当doneSignal为0时,main被唤醒继续执行。