AQS 小知识
AQS的源码也看了好几遍了,在看代码的过程中发现一些值得注意的点
1 排他锁第一次进来,不会产生队列,而是直接返回
2 第二个线程竞争锁,会产生队列,同时header是一个哨兵节点,因为唤醒逻辑是唤醒头结点的下一个节点
3 什么时候会产生CANCEL状态,注意是doAcquireInterruptibly
private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())//如果是线程中断导致的唤醒,会抛异常,就会走到finally分支 throw new InterruptedException(); } } finally { if (failed)//只有中断的情况,才有可能跳出上面的自旋,failed==true cancelAcquire(node); } }
整个类里我只看到这么一处设置CANCELLED
private void cancelAcquire(Node node) { // Ignore if node doesn't exist if (node == null) return; node.thread = null; // Skip cancelled predecessors Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; // predNext is the apparent node to unsplice. CASes below will // fail if not, in which case, we lost race vs another cancel // or signal, so no further action is necessary. Node predNext = pred.next; // Can use unconditional write instead of CAS here. // After this atomic step, other Nodes can skip past us. // Before, we are free of interference from other threads. node.waitStatus = Node.CANCELLED; // If we are the tail, remove ourselves. if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) {//如果该node不是头,该节点的pre的状态是signal或者pre通过cas能够设置成signal,那么就可以放心的修改pre的指针了,把该node剔除 Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else {//如果是header,或者cas失败了,那就唤醒它的后继节点 unparkSuccessor(node); } node.next = node; // help GC } }
4 acquireQueued 不会响应中断
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())//即使这里有中断,也只是执行interrupted = true,然后还是会再次自旋,直到获得了锁 interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
5 AQS是一个框架,作为开发者只需要实现这五个方法
1 protected boolean tryAcquire(int arg) 排他锁
2 protected boolean tryRelease(int arg) 排他锁
3 int tryAcquireShared(int arg)共享锁
4 protected boolean tryReleaseShared(int arg) 共享锁
5 isHeldExclusively
这里最特殊的就是 tryAcquireShared ,返回值是int