Java并发之CountDownLatch源码分析


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(),来清理
            }
        }



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值