超时

互斥锁和共享锁分别有自己的超时的方法:tryAcquireNanostryAcquireSharedNanos。相比于阻塞方法,超时方法接受新的参数:超时时间,在该时间内如果没有抢到锁,则返回 false 代表失败。方法的定义如下:

public final boolean tryAcquireNanos(int arg, long nanosTimeout) {...} public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) {...}

支持超时的方法主要有两点不同:

  • 每次被唤醒时都需要判断是否已经超时
  • 在休眠时也需要通过 LockSupport.parkNanos 定好闹钟

这里以 doAcquireNanos 方法看看它和 acquireQueued 的异同

private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; 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 true; } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) // 休眠之前先看是否超时 return false; if (shouldParkAfterFailedAcquire(p, node) && // 如果超时时间短,则不休眠,因为自旋效率更高 nanosTimeout > spinForTimeoutThreshold) // 休眠时要定敲钟,在 nanosTimeout 后被唤醒 LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }

因此我们看到,对超时的支持主要依赖了 LockSupport.parkNanos 的支持,它允许我们在休眠时指定时间,过了这个时间后线程会被唤醒。这样能保证 doAcquireNanos 在超时时间后可以被唤醒,检测锁状态并退出,而不是无限制地阻塞。