一、CountDownLatch简介
CountDownLatch称为闭锁,其实也是一个计数器,主要用来一个线程等待多个线程工程完成操作,仅执行一次。就像是跟团旅游,导游要等到所有的游客到齐后才会出发前往下一个景点。
示例:CountDownLatch 大小为2,在执行了两次countDown之后,整个才会执行完成,如果CountDownLatch大小为2以上,那么这个将不会正常执行完成,因为有个限制永远不会执行完成。
package cn.com.threadtx.cn.com;
import java.util.concurrent.CountDownLatch;
/**
* CountDownLatch示例
*/
public class CountDownLatchTest {
static CountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown();
System.out.println(2);
c.countDown();
}
}).start();
try {
c.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(3);
}
}
二、原理分析
CountDownLatch的实现原理也是基础AQS同步队列器,方法比较少,主要的就是await和countDown。
只有一个含参的构造方法:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
内部类:Sync继承AbstractQueuedSynchronizer
private static final class Sync extends AbstractQueuedSynchronizer {...}
主要方法:
//使当前限制在闭锁中倒计数至0之前一直等待,除非线程中断
public void await() throws InterruptedException {...}
//含超时时间
public boolean await(long timeout, TimeUnit unit)throws InterruptedException {...}
//递减闭锁中计数,如果计数到达0,则释放所有等待的线程
public void countDown() {...}
//返回当前计数
public long getCount() {...}
//返回标识此闭锁激起状态的字符串
public String toString() {...}
countDown()方法
public void countDown() {
sync.releaseShared(1);//1-1
}
1-1,调用1-2方法递减计数器,调用1-3方法修改节点状态,唤醒线程
//1-1
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//1-2
doReleaseShared();//1-3
return true;
}
return false;
}
//1-2调用内部类方法,如果计数器等于0,直接返回,如果不为空,调用CAS方法修改state的值(每次减1)
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;
}
}
}
//1-3修改节点状态,唤醒等待的线程
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);//@2唤醒线程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) //退出循环 // loop if head changed
break;
}
}
//@2唤醒线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点遍历找到最渴望被唤醒的线程
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
await()方法:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);//1-1
}
//1-1线程中断直接抛错。
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//1-2
doAcquireSharedInterruptibly(arg);//1-3
}
//1-2判断倒计数器是否已经为0,为0则返回1,不为0返回-1
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//1-3倒计数器不为,说明还有线程没有到位,需要等待线程,加入到等待队列中
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);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
参考书籍:
方志明 著《Java并发编程艺术》
Doug lea 等著《Java并发编程实战》