通过调用同步器的 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 {
}// 检查线程是否被中断,是的话直接抛出中断异常if (Thread.interrupted()) throw new InterruptedException();// 尝试获取同步状态,获取失败则进入 doAcquireNanos 方法进行获取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、独占式超时模型的流程图

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