CountDownLatch是并发包中的一个工具类,用于控制多个线程在其他任务完成后再继续执行的逻辑。
public class CountDownLatchTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
CountDownLatch latch=new CountDownLatch(2);
System.out.println(Thread.currentThread().getName()+" before latch");
Thread t2=new CountDownLatchTest().new Thread2(latch);
t2.start();
Thread t1= new CountDownLatchTest().new Thread1(latch);
t1.start();
try {
latch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" after latch");
}
public class Thread2 extends Thread{
/**
* @param cy
*/
public Thread2(CountDownLatch latch) {
super();
this.latch=latch;
}
CyclicBarrier cy;
CountDownLatch latch;
@Override
public void run(){
System.out.println("thread2 before countDown");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
latch.countDown();
System.out.println("thread2 after countDown11111");
latch.countDown();
System.out.println("thread2 after countDown222222");
}
}
public class Thread1 extends Thread{
/**
* @param cy
*/
public Thread1(CountDownLatch latch) {
super();
this.latch=latch;
}
CyclicBarrier cy;
CountDownLatch latch;
@Override
public void run(){
System.out.println("thread1 before await");
try {
latch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("thread1 after await11111");
}
}
}
如上述代码所示,对于一个初始化参数为2的CountdownLatch,main线程和Thread1调用了CountdownLatch的await()方法后会阻塞,直到等待Thread2的CountdownLatch执行完两次countDown(),这两个线程才会继续执行。
查看CountdownLatch的源码可知,其实现基于AQS。其两个基本操作await和countdown都是对基于AQS的内部类Sync的操作。通过判断Sync的state(其初始化值就是CountDownLatch latch=new CountDownLatch(i); 中的i),来确定是否对线程的阻塞和释放。
先看其内部类Sync的实现:
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//共享式获取同步状态,当getState=0时,说明state值已经被countdown消费完,可以获取同步状态,返回1,否则返回-1
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//共享式释放同步状态,当通过cas原子操作设置--state=0时候,返回true
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;
}
}
}
主要关注tryAcquireShared和tryReleaseShared方法
下面关注await方法。其最终会调用AQS的acquireSharedInterruptibly()方法:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0) //如果小于0,表明state的值没消费完
doAcquireSharedInterruptibly(arg); // 那就将当前线程放入abs队列,并自旋式的获取同步状态
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED); //将当前线程放入同步队列,并返回节点
boolean failed = true;
try {
for (;;) { //自旋操作
final Node p = node.predecessor();
if (p == head) { // //当前节点的前一个节点是头节点的话
int r = tryAcquireShared(arg); //那就尝试获取同步状态,当state==0时返回1,否则返回-1.
if (r >= 0) { //如果大于0,表明countdown消费完state的值
setHeadAndPropagate(node, r); //释放当前节点并传播
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && //如果state!=0,则让线程进入等待队列
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
调用countdown操作的源码如下:会调用abs的下面方面
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) { //tryReleaseShared每次操作都是将state--,如果state=0,则返回true,说明state已经被countdown消费完,所有等待线程都释放。
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
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;
}
}