硬核干货:5W字17张高清图理解同步器框架AbstractQueuedSynchronizer (7)

接着分析一下release(int arg)的实现:

// 释放资源 public final boolean release(int arg) { // 尝试释放资源 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } // 尝试释放资源,独占模式下,尝试通过重新设置status的值从而实现释放资源的功能 // 这个方法必须由子类实现 protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } // 解除传入节点(一般是头节点)的第一个后继节点的阻塞状态,当前处理节点的等待状态会被CAS更新为0 private void unparkSuccessor(Node node) { int ws = node.waitStatus; // 当前处理的节点(一般是头节点)状态小于0则直接CAS更新为0 if (ws < 0) node.compareAndSetWaitStatus(ws, 0); Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; // 如果节点的第一个后继节点为null或者等待状态大于0(取消),则从等待队列的尾节点向前遍历, // 找到最后一个不为null,并且等待状态小于等于0的节点 for (Node p = tail; p != node && p != null; p = p.prev) if (p.waitStatus <= 0) s = p; } // 解除上面的搜索到的节点的阻塞状态 if (s != null) LockSupport.unpark(s.thread); }

接着用上面的图:

j-a-q-s-8

上面图中thread-2晋升为头节点的第一个后继节点,等待下一个release()释放资源唤醒之就能晋升为头节点,一旦晋升为头节点也就是意味着可以解除阻塞继续运行。接着我们可以看acquire()的响应中断版本和带超时的版本。先看acquireInterruptibly(int arg):

public final void acquireInterruptibly(int arg) throws InterruptedException { // 获取并且清空线程中断标记位,如果是中断状态则直接抛InterruptedException异常 if (Thread.interrupted()) throw new InterruptedException(); // 如果获取资源失败 if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } // 独占模式下响应中断的获取资源方法 private void doAcquireInterruptibly(int arg) throws InterruptedException { // 基于当前线程新增一个独占的Node节点进入同步等待队列中 final Node node = addWaiter(Node.EXCLUSIVE); try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return; } // 获取资源失败进入阻塞状态 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 解除阻塞后直接抛出InterruptedException异常 throw new InterruptedException(); } } catch (Throwable t) { cancelAcquire(node); throw t; } }

doAcquireInterruptibly(int arg)方法和acquire(int arg)类似,最大的不同点在于阻塞线程解除阻塞后并不是正常继续运行,而是直接抛出InterruptedException异常。最后看tryAcquireNanos(int arg, long nanosTimeout)的实现:

// 独占模式下尝试在指定超时时间内获取资源,响应线程中断 public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); } // 独占模式下带超时时间限制的获取资源方法 private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { // 超时期限小于0纳秒,快速失败 if (nanosTimeout <= 0L) return false; // 超时的最终期限是当前系统时钟纳秒+外部指定的nanosTimeout增量 final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return true; } // 计算出剩余的超时时间 nanosTimeout = deadline - System.nanoTime(); // 剩余超时时间小于0说明已经超时则取消获取 if (nanosTimeout <= 0L) { cancelAcquire(node); return false; } // 这里会判断剩余超时时间大于1000纳秒的时候才会进行带超时期限的线程阻塞,否则会进入下一轮获取尝试 if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } catch (Throwable t) { cancelAcquire(node); throw t; } }

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

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