java的Lock体系相比synchronize具有一些更方便的特性,比如锁可以响应中断以及超时等待等特性,我们可以通过看源码来看看响应中断是如何实现的。
一、可中断式获取锁
可响应中断式锁调用的方法是lock.lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
而lockInterruptibly()会调用AQS的acquireInterruptibly()模板方法
public final void acquireInterruptibly(int arg)
throws InterruptedException {
//增加了对中断状态的判断,
//如果检测线程中断状态改变,抛出中断异常后方法直接退出
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//线程获取锁失败
doAcquireInterruptibly(arg);
}
在获取同步状态失败后就会调用doAcquireInterruptibly方法:
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
// 将节点插入到同步队列中
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;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//线程被阻塞时,若检测到中断则抛出中断异常后退出
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这段代码以中断模式独占获取同步状态,与acquire方法逻辑几乎一致,唯一的区别是当parkAndCheckInterrupt返回true时即线程阻塞时该线程被中断,代码抛出被中断异常。
二、超时等待获取锁
超时等待获取锁就是在中断获取锁的基础上增加超时功能
调用lock.tryLock(timeout,TimeUnit)方法实现超时等待获取锁的效果,该方法会在三种情况下才会返回:
- 在超时时间内,当前线程成功获取了锁;
- 当前线程在超时时间内被中断;
- 超时时间结束,仍未获得锁返回false
tryLock(timeout,TimeUnit)源码
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
该方法本质调用AQS的模板方法public final boolean tryAcquireNanos(int arg, long nanosTimeout)
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
最终调用doAcquireNanos()方法实现超时等待效果
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 传入时间小于0 方法直接退出,线程获取锁失败
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)
// 在超时时间内仍未被唤醒,线程退出
LockSupport.parkNanos(this, nanosTimeout);
//线程被中断抛出被中断异常
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
逻辑图如下