CountDownLanch 深度解析

CountDownLatch 深度解析

涉及到到函数

public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
}
private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;		// 全到时返回1,未全到时返回-1
        }

		// 比如是这样一个场景:
		// 一个组长等待三个员工,这个函数作用就是修改state变量
		// 对应于这个事例就是修改已到达到员工数 
        protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;    // 当所有员工到达时满足为条件
            }
        }
}
public void countDown() {
        sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {   // 只有满足条件时
            doReleaseShared();   
            return true;
        }
        return false;
}
//尝试去唤醒正在等待的线程,等待操作在await方法内
private void doReleaseShared() {
        for (;;) {
            Node h = head;
            // 当头节点被设置后并且不止一个节点时才会进去,当enq操作完成之后就能满足这个条件
            if (h != null && h != tail) {		
                int ws = h.waitStatus;			
                if (ws == Node.SIGNAL) {		// 当节点的状态为SIGNAL时才可以被唤醒
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))		// CAS修改状态
                        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;
        }
}
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);    // 响应中断的共享获取
}
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)			// 当员工没有全到时
            // 已经到到员工需要等待其他员工到来,如何等待呢?
            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);
                    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);
        }
}
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;		// 当第一次进来时都为空
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);			// 入队操作
        return node;
}
private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
            	// 初始化时只有一个节点,此时头和尾都指向这个节点,但是此时不会退出
                if (compareAndSetHead(new Node()))	
                    tail = head;
            } else {
                node.prev = t;		// 当头节点设置成功后,
                // 设置尾节点,此时虽然看起来是有两个节点来,头尾节点的指向不同,但是没看到尾节点的创建**?**
                if (compareAndSetTail(t, node)) {	
                    t.next = node;
                    // 尾节点设置成功并返回,此时头节点指向的是链表中最右边,尾节点在最左边,并且好像不是在一个节点**?**
                    return t;		
                }
            }
        }
}
private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
       
       // propagate 大于等于0才会进来,但是只可能是1 和 -1
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();	// 唤醒等待的线程
        }
}
private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
}
// 唤醒线程
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)		// 向pre方向走
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
}

所以整体的故事是这个样子的:
组长到来,然后等待其他组员的到来,比如现在有三个员工;员工到来总有先后,先来的总是需要等待的(await,假设先来的困了睡着了),当所有员工到达时需要唤醒之前等待的员工,但是员工等待的数量又不止一个,此时就需要传播,即需要把“起来开会”的消息传播给睡着的员工,然后就会一直判断它是否是第一个需要被唤醒的员工(即是否是头节点),如果不是就继续睡觉(挂起),如果是就唤醒,此时需要说明的是后来的节点是在头节点的pre域存着,所以当判断的节点的next为空时就可以被唤醒;然后唤醒的员工就开始干活,当所有的员工干完自己的活后,组长说今天可以下班了(感觉例子举的不太恰当?,听过程,禁止学习呦!)

个人理解,如有错误请在下方留言不吝指正,感谢?

你可能感兴趣的:(Java)