什么是中断
参考 线程的中断 & Interrupt
AQS如何响应
AQS响应中断的本质是让运行的线程能够从阻塞中唤醒并执行完毕。这其中包括2点
-
1 线程能从阻塞中唤醒,AQS使用LockSupport.park(this)阻塞线程。而此方法是支持中断。
-
2 线程能执行完毕正常退出,在AQS只有获取同步状态才能正常退出自旋循环。需要退出就需要在中断时抛出异常。
独占同步响应中断
/**
* 功能同acquire,可以响应线程中断
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
/** 测试当前线程是否已经中断(如果已经中断会将中断标识重新设置为false)**/
if (Thread.interrupted())
/** 抛出异常 **/
throw new InterruptedException();
/**
*子类实现tryAcquire能否获取的独占式同步状态
*如果返回true则获取同步状态成功方法直接返回
*如果返回false则获取同步状态失败进入if语句
*/
if (!tryAcquire(arg))
/** 创建一个独占式节点node,添加到同步队列尾部.进入自旋,同步队列头部后置第一个节点线程尝试获取同步状态,成功则设置其为head节点.失败则阻塞
* 如果发生中断,抛出异常线程退出自旋 **/
doAcquireInterruptibly(arg);
}
/**
* 独占获取同步状态,响应中断
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
/** addWaiter创建一个独占式节点node,添加到同步队列尾部.**/
final Node node = addWaiter(Node.EXCLUSIVE);
/** 执行是否发生异常 **/
boolean failed = true;
try {
/** 进入自旋 **/
for (;;) {
/** 1. 获得当前节点的先驱节点 **/
final Node p = node.predecessor();
/** 如果当前节点的先驱节点是头结点并且成功获取同步状态 **/
if (p == head && tryAcquire(arg)) {
/** 并将当前节点设置为head节点 **/
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
/** 2.2 获取锁失败,线程阻塞(可响应线程被中断), 如果是中断响应设置interrupted = true;
* 抛出异常,中断导致退出自旋线程不在等待!!**/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
/** 发生异常,将当前节点等待状态设置为取消**/
if (failed)
cancelAcquire(node);
}
}
共享同步响应中断
/**
* 共享式获取同步状态,如果当前线程获取同步状态成功则直接返回,
* 如果获取失败则线程阻塞,并插入同步队列进行.等待调用releaseShared
* 释放同步状态时,重新尝试获取同步状态。成功则,同时会通知后置节点线程从阻塞中唤醒,
* 获取同步状态并返回,失败则阻塞等待下次release
*/
public final void acquireShared(int arg) {
/**
*子类实现tryAcquireShared能否获取的共享式同步状态
*如果返回>=0则获取同步状态成功方法直接返回
*如果返回< 0则获取同步状态失败进入if语句
*/
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
/**
* 创建一个共享式节点node,添加到同步队列尾部.
* 进入自旋,找到CLH头部后置第一个节点,尝试获取同步状态,成功则设置其为新head节点,
* 并通知后置节点线程从阻塞中唤醒竞争同步状态.失败则阻塞.
*/
private void doAcquireShared(int arg) {
/** 创建一个共享式节点node,添加到同步队列尾部..**/
final Node node = addWaiter(Node.SHARED);
/** 执行是否发生异常 **/
boolean failed = true;
try {
/** 标识是否被中断 **/
boolean interrupted = false;
/** 进入自旋 **/
for (;;) {
/** 1. 获得当前节点的先驱节点 **/
final Node p = node.predecessor();
if (p == head) {
/** 如果当前节点的先驱节点是头结点并且成功获取同步状态 **/
int r = tryAcquireShared(arg);
if (r >= 0) {
/** 将当前节点设置为head,同时只要同步队列中存在等待的节点,
* 且节点为共享节点则唤醒head节点后置节点阻塞去竞争同步状态. **/
setHeadAndPropagate(node, r);
p.next = null; // help GC
/** 如果当前线程中断 **/
if (interrupted)
/** 中断当前线程 **/
selfInterrupt();
failed = false;
return;
}
}
/** 获取锁失败,在shouldParkAfterFailedAcquire中设置节点的等待状态,并线程阻塞(可响应线程被中断),
* 如果是中断响应设置interrupted = true;
* 抛出异常,中断导致退出自旋线程不在等待!!**/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
/** 发生异常,将当前节点等待状态设置为取消**/
if (failed)
cancelAcquire(node);
}
}
网友评论