通常使用 ReentrantLock.tryLock 的时候,都会带上一个时间戳,如果到了时间仍然没获取锁返回 false。
不带时间戳,当前线程只会尝试获取一次锁,然后返回结果;带上时间戳,则当前线程在等待时间内会多次尝试获取锁。
这里面细节还挺多,在等待时间内,线程是否会挂起?
如果挂起,是怎么挂起的?线程如何醒来?
如果不挂起,又怎么去做?
static final long spinForTimeoutThreshold = 1000L; // java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireNanos /** * Acquires in exclusive timed mode. * * @param arg the acquire argument * @param nanosTimeout max wait time * @return {@code true} if acquired */ private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; // 计算到期时间戳 final long deadline = System.nanoTime() + nanosTimeout; // 先把节点加入到等待队列的尾部,等待队列是一个带头节点的双向链表 // node 是当前节点,node.thread 是 currentThread final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); // 如果 node 的前驱节点是头节点,则尝试修改 state 值,获取锁 if (p == head && tryAcquire(arg)) { // 这种情况只发生在队列中只有一个等待节点 setHead(node); p.next = null; // help GC failed = false; return true; } // 判断是否到期 nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) // 到期返回 false,先执行 finally 代码块 return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) // 如果 nanosTimeout 大于自旋的时间,则直接挂起线程 // 在 nanosTimeout 时间后,线程自动醒来,从此处继续执行 for 循环 // 如果 nanosTimeout 小于自旋的时间,则当前线程一直自旋,执行 for 循环,直到获取锁,或者过期返回 false LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { // 线程最终获取锁失败,把节点从等待队列中移除 if (failed) cancelAcquire(node); } }