本文源码基于Android Sdk 26,打开Android Studio,把CompileSdk改为26,即可找到Android Sdk 26对应的CountDownLatch的源码。
定义
我们看下源码的解释:A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
翻译一下:CountDownLatch是一个同步辅助类,它可以让一个或多个线程等待,直到其他线程执行完一系列操作。
常用方法
countdown():
CountDownLatch在初始化时,需要给定一个整数作为计数器。当调用countDown()时,计数器会减1。
await():
如果计数器大于0,调用await()的线程会阻塞,一直到计数器被countDown()减到0,线程才会继续执行。计数器是无法重置的,当计数器被减到0时,调用await()会直接返回。
举例
源码中已经给了一个示例:有一个Boss和几名打工人,Boss说“你们可以开始干活了”,打工人才可以开始干活。等到所有的打工人活干完了,Boss开始给打工人发盒饭。这里需要2个CountDownLatch,一个是Boss通知打工人可以开始干活了,打工人才开始干活;一个是Boss等待所有打工人活干完了,才开始发盒饭。代码如下:
public class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
}
} // return;
void doWork() {
System.out.println(Thread.currentThread().getName() + " start to do work!");
}
}
public class Driver { // ...
private static final int N = 6;//the number of workers
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
bossSaidStartWorking(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doneSignal.await(); // wait for all to finish
bossSaidTimeForDinner();
}
private static void bossSaidStartWorking(){
System.out.println("Now you can start working!");
}
private static void bossSaidTimeForDinner(){
System.out.println("Now you can start eating!");
}
}
运行后的打印如下:
Now you can start working!
Thread-2 start to do work!
Thread-7 start to do work!
Thread-3 start to do work!
Thread-6 start to do work!
Thread-4 start to do work!
Thread-5 start to do work!
Now you can start eating!
如果有个打工人活一直没干完,Boss不可能让所有人一直等他,饭都凉了。于是Boss通知大家:我最多等30分钟,我们就开始吃饭。我们把doneSignal.await();改成doneSignal.await(30, TimeUnit.MINUTES);即可实现该需求。
实现原理
CountDownLatch类中的await()和countDown()实际操作的对象是内部类Sync,而Sync继承自 AbstractQueuedSynchronizer类,其中维护了一个整数state,使用volatile进行了修饰:
private volatile int state;
在 countDown()中调用了Sync实例的releaseShared(1):
public void countDown() {
sync.releaseShared(1);
}
其中的releaseShared(1),先对计数器进行减1操作,如果减1后的计数器为0,唤醒被await()阻塞的所有线程,具体是这样的:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { //对计数器进行减1操作
doReleaseShared();//如果计数器为0,唤醒被await()阻塞的所有线程
return true;
}
return false;
}
其中的tryReleaseShared()的具体实现在Sync类中,它先获取当前计数器的值,如果计数器为0时,就直接返回;如果不为0,使用CAS方法对计数器进行减1操作:
protected boolean tryReleaseShared(int releases) {
for (;;) {//死循环,如果CAS操作失败就会不断继续尝试。
int c = getState();//获取当前计数器的值。
if (c == 0)// 计数器为0时,就直接返回。
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))// 使用CAS方法对计数器进行减1操作
return nextc == 0;//如果操作成功,返回计数器是否为0
}
}
在await()中,只调用了Sync实例的acquireSharedInterruptibly(1):
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
其中acquireSharedInterruptibly(1),判断计数器是否为0,如果不为0则阻塞当前线程:
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//判断计数器是否为0
doAcquireSharedInterruptibly(arg);//如果不为0则阻塞当前线程
}
其中tryAcquireShared()是AbstractQueuedSynchronizer中的一个模板方法,其具体实现在Sync类中,其主要是判断计数器是否为零,如果为零则返回1,如果不为零则返回-1:
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
参考:https://blog.csdn.net/heihaozi/article/details/105738230