利用多线程内的共享内存特性,获取计数值,每次调用countDown
方法,就会判断是否为0,如果不是0,就会自动减 1,直到为 0 为止。
CountDownLatch
有一个内部类叫做Sync,它继承了AbstractQueuedSynchronizer
类,其中维护了一个整数state
,并且保证了修改state的可见性和原子性。(其实就是需要在创建CountDownLatch
实例时,需要传递一个整形 int
的参数,用来当做计数值的初始值,当然这个计数值,肯定不会动态变化的就是)
如下实例化 CountDownLatch
类,传递了 3 这个整形数字,作为计数值,后面就是一个一个的减 1 直到 0 为止。
CountDownLatch latch = new CountDownLatch(3);
具体的 CountDownLatch
类代码解释如下:
创建CountDownLatch
实例时,也会创建一个Sync的实例,同时把计数器的值传给Sync实例,具体是这样的:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
在 countDown
方法中,只调用了Sync实例的releaseShared
方法,具体是这样的:
public void countDown() {
sync.releaseShared(1);
}
其中的releaseShared
方法,先对计数器进行减1操作,如果减1后的计数器为0,唤醒被await方法阻塞的所有线程,具体如下的:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { //对计数器进行减一操作
doReleaseShared();//如果计数器为0,唤醒被await方法阻塞的所有线程
return true;
}
return false;
}
其中的tryReleaseShared
方法,先获取当前计数器的值,如果计数器为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
方法,具体是这样的:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
其中acquireSharedInterruptibly
方法,判断计数器是否为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;
}
腾讯面试居然跟我扯了半小时的CountDownLatch
java 多线程按顺序执行、顺序获取结果