CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。内部采用的公平锁和共享锁的机制实现。
一、CountDownLatch的构造函数(采用的是一种公平锁机制)
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
二、CountDownLatch的函数列表
void await():如果当前count大于0,当前线程将会wait,直到count等于0或者中断。PS:当count等于0的时候,再去调用await(),
线程将不会阻塞,而是立即运行。后面可以通过源码分析得到。
boolean await(long timeout, TimeUnit unit):使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
void countDown(): 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
long getCount() :获得计数的数量
String toString() :没什么好说的
三、CountDownLatch的数据结构:
四、await()的分析
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
AQS:acquireSharedInterruptibly(int arg)
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//判断是否发生中断
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//注意:-1表示获取到了共享锁,1表示没有获取共享锁
doAcquireSharedInterruptibly(arg);
}
CountDownLatch$Sync:int tryAcquireShared(int acquires)
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;//这里的state就是最开始new CountDownLatch(int count),count等于state
}
如果获取共享锁继续调用doAcquireSharedInterruptibly(arg)
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);//就判断尝试获取锁
/*
这里要注意一下r的值就2种情况-1和1:
情况1.r为-1,latch没有调用countDown(),state是没有变化的导致state一直大于0或者调用了countDown(),但是state不等于0,直接在for循环中等待
情况2.r为1,证明countDown(),已经减到0,当前线程还在队列中,state已经等于0了.接下来就是唤醒队列中的节点
*/
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);
}
}
如果这里多个线程wait之间没有调用countDown(),线程都在等待。如下图:
setHeadAndPropagate(Node node, int propagate)
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // 记录头结点
setHead(node);//设置node为头结点
/*
*从上面传递过来 propagate = 1;
*一定会进入下面的判断
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;//获得当前节点的下一个节点,如果为最后一个节点或者,为shared
if (s == null || s.isShared())
doReleaseShared();//释放共享锁
}
}
isShared()
final boolean isShared() {
return nextWaiter == SHARED;
}
释放共享锁,通知后面的节点。
private void doReleaseShared() {
for (;;) {
Node h = head;//获得头结点
if (h != null && h != tail) {
int ws = h.waitStatus;//获取头结点的状态默认值为0
if (ws == Node.SIGNAL) {如果等于SIGNAL唤醒状态
//将头结点的状态置成0,并使用Node.SIGNAL(-1)与0比较,continue,h的状态设置为0,不会再进入if (ws == Node.SIGNAL)
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}//判断ws是否为0,并且h的状态不等于0,这里是个坑啊,ws等于0,h就是0啊,所以if进不来的,并设置节点为PROPAGATE
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
如果头结点等于h,其实也没有一直loop,由于上面写的Node h = head,就算前面的条件都不满足,这里一定会break
if (h == head) // loop if head changed
break;
}
}
compareAndSetWaitStatus(Node,int,int)
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);//使用JNI调用c++代码
}
看 j vm的代码 hotspot-9646293b9637\src\share\vm\runtime unsafe.cpp
c++:comapreAndSwapInt
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
//获得对象地址指针
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
//比较原来的状态值是否和期待的状态值一样
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
atomic.cpp
c++:cmpxchg 根据(update参数来看)代码应该赋值后在比较,但是while比较之后会再一次赋值。
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
assert(sizeof(jbyte) == 1, "assumption.");
uintptr_t dest_addr = (uintptr_t)dest;
uintptr_t offset = dest_addr % sizeof(jint);
volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
jint cur = *dest_int;
//保存原来的值
jbyte* cur_as_bytes = (jbyte*)(&cur);
jint new_val = cur;
jbyte* new_val_as_bytes = (jbyte*)(&new_val);
//更新状态值
new_val_as_bytes[offset] = exchange_value;
//原来的状态置和比较的值进行比较
while (cur_as_bytes[offset] == compare_value) {
jint res = cmpxchg(new_val, dest_int, cur);
if (res == cur) break;
cur = res;
new_val = cur;
new_val_as_bytes[offset] = exchange_value;
}
//还回原来的状态值
return cur_as_bytes[offset];
}
五、countDown()分析
public void countDown() {
sync.releaseShared(1);//每次释放一个
}
boolean releaseShared(int arg)
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//尝试释放共享锁
doReleaseShared();//释放共享锁,上面有泛型不在重复
//由于state等于0,所以从队列中,从头结点一个接着一个退出(break),for循环等待,
return true;
}
return false;
}
tryReleaseShared(int)
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))//比较并设置state
return nextc == 0; //等于0的时候,肯定还有一些Node在队列中,所以要调用doReleaseShared(),来清理
}
}