一文带你学会AQS和并发工具类的关系2 (2)

如果不是头节点或者tryAcquire()方法执行失败执行下面的更加繁琐的方法shouldParkAfterFailedAcquire(p, node),如果该方法返回true才会执行到下面的parkAndCheckInterrupt()方法,这两个方法都是AQS中的方法。

## AbstractQueuedSynchronizer private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { // 获取前驱节点的状态 int ws = pred.waitStatus; // 如果前驱节点的状态为SIGNAL那么直接就可以沉睡了,因为如果一个节点要是进入 // 阻塞队列的话,那么他的前驱节点的waitStatus必须是SIGNAL状态。 if (ws == Node.SIGNAL) return true; // 如果前驱节点不是Node.SIGNAL状态就往前遍历一值寻找节点的waitStatus必须 // 是SIGNAL状态的节点 if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { // 如果没有找到符合条件的节点,那么就将当前节点的前驱节点的waitStatus // 设置成SIGNAL状态 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }

如果返回的值是false,就要注意此时又继续进入了下一次死循环中,因为如果往前遍历的过程中有可能他的前驱节点变成了头节点,那么就可以再次的获取锁,如果不是的话那么只能
执行parkAndCheckInterrupt()方法进行线程的挂起了。

private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); } 5.取消请求

无论如何最终都走到了cancelAcquire方法

private void cancelAcquire(Node node) { if (node == null) return; node.thread = null; Node pred = node.prev; // 跳过所有取消请求的节点 while (pred.waitStatus > 0) node.prev = pred = pred.prev; Node predNext = pred.next; // 将当前节点设置成取消状态,为了后续遍历跳过我们 node.waitStatus = Node.CANCELLED; // 如果当前节点是尾节点,并且将当前节点的前驱节点设置成尾节点成功 if (node == tail && compareAndSetTail(node, pred)) { // 当前节点的前驱节点的后续节点为空 compareAndSetNext(pred, predNext, null); } else { int ws; // 如果前驱节点不是头节点 if (pred != head && // 前驱节点的状态是Node.SIGNAL或者前驱节点的waitStatus设置 // 成Node.SIGNAL ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && // 前驱节点的thread 不为空 pred.thread != null) { // 获取当前节点的后继节点 Node next = node.next; // 如果后继节点不为空,并且后继节点waitStatus 小于0 if (next != null && next.waitStatus <= 0) // 将当前节点的后继节点设置成当前节点的前驱节点的后继节点 compareAndSetNext(pred, predNext, next); } else { // 如果上面当前节点的前驱节点是head或者其他条件不满足那么就唤醒当前节点 unparkSuccessor(node); } node.next = node; // help GC } }

unparkSuccessor(node)唤醒当前节点,该方法也是AbstractQueuedSynchronizer中的方法

private void unparkSuccessor(Node node) { // 获取当前节点的状态 int ws = node.waitStatus; // 如果当前节点状态小于0那么设置成0 if (ws < 0) compareAndSetWaitStatus(node, ws, 0); // 获取当前节点的后继节点 Node s = node.next; // 如果后继节点为空,或者后继节点的状态小于0 if (s == null || s.waitStatus > 0) { // 后继节点置为null。视为取消请求的节点 s = null; // 获取尾节点,并且尾节点不为空,不是当前节点,那么就往前遍历寻找 // 节点waitStatus 状态小于0的节点赋予给当前节点的后继节点 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) // 唤醒后继节点 LockSupport.unpark(s.thread); } 3.获取锁的流程图

流程图和上一篇非公平锁的获取流程图十分相似只有一点点区别这里就不过多的描述了。

4.释放锁的实现 4.1释放锁代码分析

尝试释放此锁。如果当前线程是此锁的持有者,则保留计数将减少。 如果保持计数现在为零,则释放锁定。 如果当前线程不是此锁的持有者,则抛出IllegalMonitorStateException。

## ReentrantLock public void unlock() { sync.release(1); }

sync.release(1) 调用的是AbstractQueuedSynchronizer中的release方法

## AbstractQueuedSynchronizer public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }

分析tryRelease(arg)方法

图片

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

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