万字超强图文讲解AQS以及ReentrantLock应用(建议收藏) (7)

第一,先回看节点加入队列的情景:

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; }

节点入队并不是原子操作,代码第6、7行

node.prev = pred; compareAndSetTail(pred, node)

这两个地方可以看作是尾节点入队的原子操作,如果此时代码还没执行到 pred.next = node; 这时又恰巧执行了unparkSuccessor方法,就没办法从前往后找了,因为后继指针还没有连接起来,所以需要从后往前找

第二点原因,在上面图解产生 CANCELLED 状态节点的时候,先断开的是 Next 指针,Prev指针并未断开,因此这也是必须要从后往前遍历才能够遍历完全部的Node

同步状态至此就已经成功释放了,之前获取同步状态被挂起的线程就会被唤醒,继续从下面代码第 3 行返回执行:

private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }

继续返回上层调用栈, 从下面代码15行开始执行,重新执行循环,再次尝试获取同步状态

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; } } finally { if (failed) cancelAcquire(node); } }

到这里,关于独占式获取/释放锁的流程已经闭环了,但是关于 AQS 的另外两个模版方法还没有介绍

响应中断

超时限制

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

独占式响应中断获取同步状态

故事要从lock.lockInterruptibly() 方法说起

public void lockInterruptibly() throws InterruptedException { // 调用同步器模版方法可中断式获取同步状态 sync.acquireInterruptibly(1); }

有了前面的理解,理解独占式可响应中断的获取同步状态方式,真是一眼就能明白了:

public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // 尝试非阻塞式获取同步状态失败,如果没有获取到同步状态,执行代码7行 if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }

继续查看 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()) // 获取中断信号后,不再返回 interrupted = true 的值,而是直接抛出 InterruptedException throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }

没想到 JDK 内部也有如此相近的代码,可响应中断获取锁没什么深奥的,就是被中断抛出 InterruptedException 异常(代码第17行),这样就逐层返回上层调用栈捕获该异常进行下一步操作了

趁热打铁,来看看另外一个模版方法:

独占式超时限制获取同步状态

这个很好理解,就是给定一个时限,在该时间段内获取到同步状态,就返回 true, 否则,返回 false。好比线程给自己定了一个闹钟,闹铃一响,线程就自己返回了,这就不会使自己是阻塞状态了

既然涉及到超时限制,其核心逻辑肯定是计算时间间隔,因为在超时时间内,肯定是多次尝试获取锁的,每次获取锁肯定有时间消耗,所以计算时间间隔的逻辑就像我们在程序打印程序耗时 log 那么简单

nanosTimeout = deadline - System.nanoTime()

故事要从 lock.tryLock(time, unit) 方法说起

public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { // 调用同步器模版方法,可响应中断和超时时间限制 return sync.tryAcquireNanos(1, unit.toNanos(time)); }

来看 tryAcquireNanos 方法

public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }

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

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