综述
CountDownLatch也叫闭锁,是一种倒数计数器。CountDownLatch等待多个线程执行完毕后才做一件事。CountDownLatch是比调用线程的join()方法更好的选择。
CountDownLatch的使用
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountdownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(3);
ExecutorService executorService = Executors.newFixedThreadPool(3);
try {
executorService.execute(new MyThread(countDownLatch,5000));
executorService.execute(new MyThread(countDownLatch,5000));
executorService.execute(new MyThread(countDownLatch,10000));
System.out.println(Thread.currentThread().getName() + " starting!");
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "ending");
}finally {
assert executorService!=null;
executorService.shutdown();
}
}
public static class MyThread extends Thread{
private CountDownLatch countDownLatch;
private long timeOut;
public MyThread(CountDownLatch countDownLatch,long timeOut){
this.countDownLatch = countDownLatch;
this.timeOut = timeOut;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " starting!");
try {
Thread.sleep(timeOut);
System.out.println(Thread.currentThread().getName() + "ending");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
}
}
CountDownLatch类图
CountDownLatch类的声明
public class CountDownLatch
CountDownLatch内部类Sync
/**
*Sync使用AQS的state表示count
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造器
Sync(int count) {
setState(count);
}
//返回count值,即state值
int getCount() {
return getState();
}
//重写AQS中的tryAcquireShared()方法
protected int tryAcquireShared(int acquires) {
//如果state等于0返回1否则返回-1
return (getState() == 0) ? 1 : -1;
}
//重写AQS中tryReleaseShared()方法
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
//获取state状态
int c = getState();
//如果state等于0
if (c == 0)
//返回false
return false;
//如果state不等于0
//state减1
int nextc = c-1;
//CAS更新state成功
if (compareAndSetState(c, nextc))
//返回nextc是否等于0
return nextc == 0;
}
}
}
CountDownLatch.await()方法
当前线程调用了CountDownLatch对象的await方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回:
- 当所有的线程都调用了CountDownLatch对象的countDown()方法后,也就是计数器值为0的时候
- 其他线程调用了当前线程的interrupt()方法中断了当前线程,当前线程会抛出InterruptedException异常后返回
public void await() throws InterruptedException {
//调用AQS中的acquireSharedInterruptibly()方法
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被中断则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//如果tryAcquireShared()小于0,进入AQS的同步队列
if (tryAcquireShared(arg) < 0)
//调用doAcquireSharedInterruptibly()进入同步队列
doAcquireSharedInterruptibly(arg);
}
//tryAcquireShared()方法由子类实现
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
//重写AQS中的tryAcquireShared()方法
protected int tryAcquireShared(int acquires) {
//如果state等于0返回1否则返回-1
return (getState() == 0) ? 1 : -1;
}
Sync类的tryAcquireShared()方法在state等于0时返回1,否则返回-1。
回到AQS的acquireSharedInterruptibly()方法。当Sync类的doAcquireSharedInterruptibly()方法返回,即await()方法返回。
当Sync类的tryAcquireShared()返回-1,则将会执行doAcquireSharedInterruptibly()方法。doAcquireSharedInterruptibly()会造成线程等待,如果线程在同步队列获取到资源后,也会造成在共享模式下对资源获取成功的传播行为。
await()方法委托Sync调用了AQS的acquireSharedInterruptibly()方法,该方法的特点是线程获取资源的时候可以被中断,并且获取到的资源是共享资源,这里为什么要调用AQS的这个方法,而不是调用独占锁的acquireInterrutibly()方法呢?这是因为这里状态值需要的并不是非0即1的效果,而是和初始化时候指定的计数器值有关系,比如你初始化的时候计数器值为8,那么state的值应该就有0到8的状态,而不是只有0和1的独占效果。
这里await()方法调用acquireSharedInterruptibly()的时候传递的是1,就是说明要获取1个资源,而这里计数器值是资源总数,也就是意味着是让总的资源数减1,acquireSharedInterruptibly()内部首先判断如果当前线程被中断了则抛出异常,否则调用Sync实现的tryAcquireShared()方法看当前状态值(计数器值)是否为0。如果为0则当前线程的await()方法直接返回,否则调用AQS的doAcquireSharedInterruptibly()让当前线程阻塞。另外调用tryAcquireShared()方法仅仅是检查当前状态值是不是为0,并没有调用CAS让当前状态值减去1。
CountDownLatch.await(long timeout,TimeUnit unit)方法
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
AQS中的tryAcquireSharedNanos()方法如下:
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//如果tryAcquireShared()方法返回大于0,则返回
//否则调用doAcquireSharedNanos()方法
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
doAcquireSharedNanos()方法代码如下:
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
当线程调用了CountDownLatch对象的该方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回:
- 当所有线程都调用了CountDownLatch对象的countDown()方法后,也就是计数器值为0的时候,这时候返回true
- 设置的timeout时间到了,因为超时而返回false
- 其他线程调用了当前线程的interrupt()方法中断了当前线程,当前线程会抛出InterruptedException异常后返回
CountDownLatch.countDown()方法
当前线程调用了该方法后,会递减计数器的值,递减后如果计数器为0则会唤醒所有调用await()方法而被阻塞的线程,否则什么都不做。
public void countDown() {
sync.releaseShared(1);
}
countDown()方法调用AQS的releaseShared()方法:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
releaseShared()方法调用的tryReleaseShared()方法需要子类实现:
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
CountDownLatch内部类Sync中tryReleaseShared()方法的实现如下:
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;
}
}
如果tryReleaseShared()方法返回true,则调用:
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}