AQS源码深入分析之独占模式-ReentrantLock锁特性详解 (4)

当出现异常的时候,就会调用cancelAcquire方法来处理异常,同时还有一些其他的收尾工作。其中很重要的一点是:如果是需要唤醒的节点发生了异常,那么此时需要唤醒下一个节点,以此来保证唤醒动作能够一直传播下去。

1 /** 2 * AbstractQueuedSynchronizer: 3 * 取消当前线程获取锁资源的请求,并完成一些其他的收尾工作 4 */ 5 private void cancelAcquire(Node node) { 6 //非空校验 7 if (node == null) 8 return; 9 10 //节点里面的线程清空 11 node.thread = null; 12 13 /* 14 从该节点往前寻找一个不是CANCELLED状态的节点(也就是处于正常阻塞状态的节点), 15 相当于在退出前再做次清理工作。遍历过程中如果遇到了CANCELLED节点,会被剔除出 16 CLH队列等待GC 17 这里的实现逻辑是和shouldParkAfterFailedAcquire方法中是类似的,但是有一点 18 不同的是:这里并没有pred.next = node,而是延迟到了后面的CAS操作中 19 */ 20 Node pred = node.prev; 21 while (pred.waitStatus > 0) 22 node.prev = pred = pred.prev; 23 24 /* 25 如果上面遍历时有CANCELLED节点,predNext就指向pred节点的下一个CANCELLED节点 26 如果上面遍历时没有CANCELLED节点,predNext就指向自己 27 */ 28 Node predNext = pred.next; 29 30 /* 31 将状态改为CANCELLED,也就是在取消获取锁资源。这里不用CAS来改状态是可以的, 32 因为改的是CANCELLED状态,其他节点遇到CANCELLED节点是会跳过的 33 */ 34 node.waitStatus = Node.CANCELLED; 35 36 if (node == tail && compareAndSetTail(node, pred)) { 37 //如果当前节点是最后一个节点的时候,就剔除当前节点,将tail指针指向前一个节点 38 compareAndSetNext(pred, predNext, null); 39 } else { 40 int ws; 41 //走到这里说明当前节点不是最后一个节点 42 if (pred != head && 43 ((ws = pred.waitStatus) == Node.SIGNAL || 44 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && 45 pred.thread != null) { 46 /* 47 如果head指针指向的不是pred节点,并且前一个节点是SIGNAL状态(或者可以设置为SIGNAL状态), 48 并且前一个节点的thread没被清空的话,那么只需要将pred节点和当前节点的后面一个节点连接起来就行了 49 */ 50 Node next = node.next; 51 if (next != null && next.waitStatus <= 0) 52 /* 53 这里只是设置了pred节点的next指针,而没有设置next.prev = pred。但无妨,在后续的操作中, 54 如果能走到shouldParkAfterFailedAcquire方法中,会再去修正prev指针的 55 */ 56 compareAndSetNext(pred, predNext, next); 57 } else { 58 /* 59 而如果head指针指向的是pred节点(或者pred节点的thread是为null的),那么就去唤醒当前节点的 60 下一个可以被唤醒的节点,以保证即使是在发生异常的时候,CLH队列中的节点也可以一直被唤醒下去 61 当然,如果前一个节点本身就是SIGNAL状态,也是需要唤醒下一个节点的 62 */ 63 unparkSuccessor(node); 64 } 65 66 /* 67 node.next指向自己,断开该节点,同时要保证next指针一定要有值, 68 因为后续在条件队列的isOnSyncQueue方法中会判断节点是否在CLH队列中 69 其中有一条就是以判断node.next是否为null为准则,如果不为null,就说明 70 该节点还在CLH队列中 71 */ 72 node.next = node; 73 } 74 } 3.6 unparkSuccessor方法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfypy.html