通过调用同步器的 public final boolean tryAcquireNanos(int arg, long nanosTimeout) 放,可以实现超时获取同步状态的编程范式模型,即在指定时间内获取到同步状态,超过一定时间返回false,获取同步状态成功则返回true,这是在JDK1.5 之前使用Synchronized关键字所不具备的特性。

在JDK1.5 之前使用Synchronized关键字获取不到同步状态时候会一直被阻塞在Synchronized之外,即使对该线程进行了中断操作,也仅仅是该线程的中断标记被修改,但仍然会被阻塞在Synchronized之外,继续等待获取到同步状态。但值得庆幸的是在AbstractQueueSynchronizer 中提供了 acquireInterruptibly(int arg) 方法中断处于等待状态的线程并抛出InterruptiedException。

1、独占式超时模型的实现原理

我们在实现tryLock()的时候可以通过使用同步器AQS的 public final boolean tryAcquireNanos(int arg, long nanosTimeout) 方法实现超时获取模型,其代码逻辑如下

  • 代码片段的部分逻辑已经在之前的 AbstractQueueSynchronizer 独占式同步状态的获取与释放原理 详细讲过,比如自旋获取同步状态,为何要判断前驱节点等等,如果读者阅读这部分比较吃力,可以先阅读之前的文章 ```java public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    1. // 检查线程是否被中断,是的话直接抛出中断异常
    2. if (Thread.interrupted()) throw new InterruptedException();
    3. // 尝试获取同步状态,获取失败则进入 doAcquireNanos 方法进行获取
    4. return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
    }
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (nanosTimeout <= 0L) return false;
    // 计算最终延时时间,被称为死亡线 deadline
    final long deadline = System.nanoTime() + nanosTimeout;
    // 定义一个同步队列中的节点,并添加到同步队列中
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        // 不断的自旋尝试获取同步状态
        for (;;) {
            // 和普通情况下获取同步状态一样,判断当前前驱节点是否是头结点,是的话则尝试获取同步状态
            // 不熟悉的读者可以先阅读之前的文章  https://www.zhoutao123.com/page/book/java/category/rcuy1l 的1.4 章节
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            // 计算线程延时时间
            nanosTimeout = deadline - System.nanoTime();
            // 最终超时的话直接返回false
            if (nanosTimeout <= 0L) return false;
            // 这里需要注意,如果剩余时间小于 spinForTimeoutThreshold (1S) 则线程并不会进入parkNanos() 而会快速的进行自旋
            if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            // 再次检查线程是否被中断,是的话直接抛出中断异常
            if (Thread.interrupted()) throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

- 可以看到,这里的方法实现和 acquireQueued 还是有差别的,在 `acquireQueued` 方法中如果获取不到同步状态,则会进入等待状态,而在超时获取模型中则会调用 ` LockSupport.parkNanos(this, nanosTimeout);` 方法等待一段时间
- 需要注意的是如果剩余等待时间小于spinForTimeoutThreshold(1S),此时其不会在此进入超时等待,而是不断的快速的进行自旋,这是因为如果还是进入 `LockSupport.parkNanos` 的话,超时时间会出现不精准的情况

<a name="Lj1Vx"></a>
## 2、独占式超时模型的流程图

![image.png](https://cdn.nlark.com/yuque/0/2020/png/437981/1594525879322-a3764595-32d8-4dfe-b73c-9153da6e8b91.png#align=left&display=inline&height=746&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1492&originWidth=1872&size=196466&status=done&style=stroke&width=936)


<a name="MWwBD"></a>
## 3、 实现独占式超时获取锁


```java
public class TimeoutLock {

  public static void main(String[] args) {
    TimeoutLock lock = new TimeoutLock();
    for (int i = 0; i < 10; i++) {
      Thread thread =
          new Thread(
              () -> {
                Thread currentThread = Thread.currentThread();
                String threadName = currentThread.getName();
                boolean locked = lock.tryLock();
                if (locked) {
                  System.out.println(threadName + "\t 获取锁成功");
                  lock.tryRelease();
                } else {
                  System.out.println(threadName + "\t 获取锁失败");
                }
              });

      thread.setName("线程" + i);
      thread.start();
    }
  }

  private void tryRelease() {
    sync.release(1);
  }

  private final Sync sync = new Sync();

  public boolean tryLock() {
    try {
      return sync.tryAcquireNanos(1, 1000L);
    } catch (InterruptedException e) {
      return false;
    }
  }

  public static class Sync extends AbstractQueuedSynchronizer {

    @Override
    protected boolean tryAcquire(int arg) {
      return getState() == 0 && compareAndSetState(0, arg);
    }

    @Override
    protected boolean tryRelease(int arg) {
      int current = getState();
      if (current < arg) {
        throw new IllegalMonitorStateException();
      }

      int newState = current - arg;
      compareAndSetState(current, newState);
      return true;
    }
  }
}