以前讲了AQS的独占锁的源码,这边,讲一下另一个锁的实现,共享锁,以CountDownLatch为例。node
Sync方法是内部内的方法,跟以前ReentrantLock同样。构造方法须要传入一个不小于0的整数,用于赋值给state。当其余线程调用countDown方法的时候,state的值就减一。减到0的时候,其余调用await方法将被唤醒。await方法能够被多个线程调用,调用的时候,就进入了阻塞状态,直至state为0。后面重点讲countDown和await方法。segmentfault
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } Sync(int count) { setState(count); }
咱们先看看阻塞的方法,此时要等state为0的时候,才被唤醒。oop
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);//state不为0的状况下 } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1;//当前状态为0,返回1 } private void doAcquireSharedInterruptibly(int arg)//获取共享锁,而且可被中断 throws InterruptedException { final Node node = addWaiter(Node.SHARED);//这个跟以前不一样的是Node.SHARED,加入到队列,不在说明 boolean failed = true; try { for (;;) {//自旋 final Node p = node.predecessor();//获取前面节点 if (p == head) { int r = tryAcquireShared(arg);//尝试获取锁 if (r >= 0) {//state为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); } }
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared();//为true,唤醒 return true; } return false; } //用自旋使status-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; } } 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))//经过cas把头节点的waitStatus设置为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;// } }
刚刚wait的时候,doAcquireSharedInterruptibly中若是没获取到锁,就挂起。如今status为0,就唤醒他,继续自旋,次数看下面的代码源码分析
private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below获取头结点 setHead(node);//当前节点设置为头节点 if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next;//获取下一个节点 if (s == null || s.isShared()) doReleaseShared();//唤醒下一个节点 } }
CountDownLatch,实际上就是经过先设置state,再递减,等于0的时候,唤醒其余线程。
CyclicBarrier,也是先经过设置state,每一个await递减,state不等于0的时候放入Condition,等于0的时候唤醒。
Semaphore,也是先经过设置state,每次被获取state-1,释放+1,等于0就等待,大于0就唤醒ui