tryAcquireNanos(int arg, long nanosTimeout)其实和doAcquireInterruptibly(int arg)类似,它们都响应线程中断,不过tryAcquireNanos()在获取资源的每一轮循环尝试都会计算剩余可用的超时时间,只有同时满足获取失败需要阻塞并且剩余超时时间大于SPIN_FOR_TIMEOUT_THRESHOLD(1000纳秒)的情况下才会进行阻塞。
独占模式的同步器的一个显著特点就是:头节点的第一个有效(非取消)的后继节点,总是尝试获取资源,一旦获取资源成功就会解除阻塞并且晋升为头节点,原来所在节点会移除出同步等待队列,原来的队列长度就会减少1,然后头结点的第一个有效的后继节点继续开始竞争资源。
使用独占模式同步器的主要类库有:
可重入锁ReentrantLock。
读写锁ReentrantReadWriteLock中的写锁WriteLock。
共享模式共享(SHARED)模式中的"共享"的含义是:同一个时刻,如果有一个节点所在线程获取(acuqire)原子状态status成功,那么它会解除阻塞被唤醒,并且会把唤醒状态传播到所有有效的后继节点(换言之就是唤醒整个同步等待队列中的所有有效的节点)。共享模式同步器的功能主要由下面的四个方法提供:
acquireShared(int arg):申请获取arg个原子状态status(申请成功可以简单理解为status = status - arg)。
acquireSharedInterruptibly(int arg):申请获取arg个原子状态status,响应线程中断。
tryAcquireSharedNanos(int arg, long nanosTimeout):申请获取arg个原子状态status,带超时的版本。
releaseShared(int arg):释放arg个原子状态status(释放成功可以简单理解为status = status + arg)。
先看acquireShared(int arg)的源码:
// 共享模式下获取资源 public final void acquireShared(int arg) { // 注意tryAcquireShared方法值为整型,只有小于0的时候才会加入同步等待队列 if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } // 共享模式下尝试获取资源,此方法需要由子类覆盖 protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); } // 共享模式下获取资源和处理同步等待队列的方法 private void doAcquireShared(int arg) { // 基于当前线程新建一个标记为共享的新节点 final Node node = addWaiter(Node.SHARED); boolean interrupted = false; try { for (;;) { final Node p = node.predecessor(); // 如果当前节点的前驱节点是头节点 if (p == head) { // 每一轮循环都会调用tryAcquireShared尝试获取资源,除非阻塞或者跳出循环 int r = tryAcquireShared(arg); if (r >= 0) { // <= tryAcquireShared方法>=0说明直资源获取成功 // 设置头结点,并且传播获取资源成功的状态,这个方法的作用是确保唤醒状态传播到所有的后继节点 // 然后任意一个节点晋升为头节点都会唤醒其第一个有效的后继节点,起到一个链式释放和解除阻塞的动作 setHeadAndPropagate(node, r); p.next = null; // help GC return; } } // 判断获取资源失败是否需要阻塞,这里会把前驱节点的等待状态CAS更新为Node.SIGNAL if (shouldParkAfterFailedAcquire(p, node)) interrupted |= parkAndCheckInterrupt(); } } catch (Throwable t) { cancelAcquire(node); throw t; } finally { if (interrupted) selfInterrupt(); } } // 设置同步等待队列的头节点,判断当前处理的节点的后继节点是否共享模式的节点,如果共享模式的节点, // propagate大于0或者节点的waitStatus为PROPAGATE则进行共享模式下的释放资源 private void setHeadAndPropagate(Node node, int propagate) { // h为头节点的中间变量 Node h = head; // 设置当前处理节点为头节点 setHead(node); // 这个判断条件比较复杂:入参propagate大于0 || 头节点为null || 头节点的状态为非取消 || 再次获取头节点为null || 再次获取头节点不为取消 if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; // 当前节点(其实已经成为头节点)的第一个后继节点为null或者是共享模式的节点 if (s == null || s.isShared()) doReleaseShared(); } } // Release action for shared mode:共享模式下的释放资源动作 private void doReleaseShared() { for (;;) { Node h = head; // 头节点不为null并且不为尾节点 if (h != null && h != tail) { int ws = h.waitStatus; // 如果头节点等待状态为SIGNAL(-1)则CAS更新它为0,更新成功后唤醒和解除其后继节点的阻塞 if (ws == Node.SIGNAL) { if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0)) continue; // 唤醒头节点的后继节点 unparkSuccessor(h); } // 如果头节点的等待状态为0,则CAS更新它为PROPAGATE(-3) else if (ws == 0 && !h.compareAndSetWaitStatus(0, Node.PROPAGATE)) continue; } // 头节点没有变更,则跳出循环 if (h == head) break; } }