目录
interrupt 对 sleep,join,wait,park 的影响
对Thread.sleep(),Thread.join(),Object.wait()的影响
Java没有提供一种安全、直接的方法来停止某个线程,而是提供了一种中断机制。中断机制是一种协作机制,不会直接暴力地终止另一个线程,而需要被中断的线程自己来处理中断信息。被中断的线程即可以选择立即停止、也可以选择稍后再停止、也可以不停止。
interrupt API
- Thread.interrupt():将某个线程的中断标志位设置成true
- Thread.isInterrupted() :判断线程的中断标志位是否是true
- Thread.interrupted() 静态方法 :判断当前线程的中断标志位是否是true,并清除中断标志位设置为false
interrupt 使用
Case 1
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (; ; ) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("t1 线程中断,结束");
} else {
System.out.println("t1 线程未中断,继续循环...");
}
}
});
t1.start();
Thread.sleep(2000);
t1.interrupt();
System.out.println("中断t1线程");
}
}
输出结果:
...
t1 线程未中断,继续循环...
t1 线程未中断,继续循环...
t1 线程未中断,继续循环...
中断t1线程
t1 线程中断,结束
t1 线程中断,结束
t1 线程中断,结束
...
Case 2
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
boolean interrupted = false;
for (; ; ) {
if (Thread.interrupted()) {
interrupted = true;
System.out.println("t1 线程中断并清除中断标志");
} else {
if (interrupted) {
System.out.println("t1 线程已中断过并清除中断标志位,继续循环...");
break;
} else {
System.out.println("t1 线程未中断过,继续循环...");
}
}
}
});
t1.start();
Thread.sleep(1000);
t1.interrupt();
System.out.println("中断t1线程");
}
}
输出结果:
...
t1 线程未中断过,继续循环...
t1 线程未中断过,继续循环...
t1 线程未中断过,继续循环...
中断t1线程
t1 线程中断并清除中断标志
t1 线程已中断过并清除中断标志位,继续循环...
...
interrupt 对 sleep,join,wait,park 的影响
对Thread.sleep(),Thread.join(),Object.wait()的影响
我们知道在写 sleep、join、wait 的时候,都需要显示捕获InterruptedException,当线线程中断时,会立即执行中断异常逻辑,并清除中断标志位。
以sleep为列:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("t1线程中断 执行sleep中断异常逻辑...");
}
});
t1.start();
Thread.sleep(3000);
t1.interrupt();
System.out.println("中断t1线程");
}
当中断t1线程后会立即输出:"t1线程中断 执行sleep中断异常逻辑..."
对LockSupport.park()的影响
当t1线程LockSupport.park()阻塞时,调用t1.interrutp()会唤醒t1线程,不会清除中断标志位。
我们来看一段ReentrantLock.lock()的源码:
//抢锁逻辑
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();//判断中断标志如果为true,则需要补中断标志
}
最后会有一个补中断标志的逻辑,why?我们再看一下acquireQueued里的逻辑
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) //重点在这里,点进去
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这里面的逻辑不细看了,重点是parkAndCheckInterrupt方法,点进去
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();//判断中断标志是否为true,并清除中断标志
}
我们看到parkAndCheckInterrupt()方法中用到了LockSupport.park()方法,即ReetrantLock抢锁失败入队后真正阻塞线程的地方。由于LockSupport.park()被唤醒有两种方式,第一种在解锁逻辑中调用LockSupport.unPark()被唤醒。还有就是当线程被中断时会唤醒线程,此时该方法返回线程中断标志位为true,并清除中断标志位,再走抢锁逻辑,如果抢锁成功了,在执行业务逻辑之前得补一下被清除的中断标志位(给业务某个地方用的)。