文章目录
- 线程中断的作用
- `java.lang.Thread`中断实现
- 一些小练习
-
- [`Thread.interrupt()` 只唤醒线程并修改中断标识](#
Thread.interrupt()只唤醒线程并修改中断标识) - [`sleep()` 清除中断状态标识](#
sleep()清除中断状态标识)
- [`Thread.interrupt()` 只唤醒线程并修改中断标识](#
- Reference
线程中断的作用
线程中断可以使一个线程从等待状态 变成就绪状态
使用线程中断,并不是要把线程给终止或是杀死,而是让线程不再继续等待,而是让线程不再继续等待,线程可以继续往下执行代码,线程发生中断后,会抛出一个中断的异常,决定如何处理就看业务代码怎么写
线程的等待状态
详细的JVM线程状态与OS线程状态相关可以看这篇,中断等待一般是从WAITING、TIME WAITING等阻塞,或者说挂起状态(感觉更准确,一般都是主动挂起,而BLOCK阻塞状态一般是竞争锁资源失败被动阻塞到队列中),恢复到RUNNABLE。
WAITING
Object.wait():使当前线程处于等待状态,直到另一个线程通过notify()显式唤醒它;Thread.join():插队,当前线程需要等待join的线程执行完毕,底层调用的是Object实例的wait方法;LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。LockSupport.unpark(), 恢复许可来唤醒
TIMED_WAITING
-
Thread.sleep(long millis):使当前线程睡眠指定时间 -
Object.wait(long timeout):线程休眠指定时间,需要与synchronized一起使用,等待期间可以通过notify()/notifyAll()唤醒;javaObject o = new Object(); synchronized (o){ o.wait();//将当前线程挂起,并释放o锁 o.notify();//唤醒一个等待在o锁等待队列的线程 o.notifyAll();//唤醒等待在o锁等待队列的所有线程 } -
Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;
上述三种方法使线程等待后,可以通过Interrupt()显示唤醒(中断等待)
线程从等待中恢复
- 等待超时:
Thread.sleep(long millis)等到时间了自己回复 - 得到通知:
Object.wait()线程休眠期间,其他线程可通过notify()/notifyAll()将其唤醒; - 使用中断:
Interrupt()
java.lang.Thread中断实现
相关方法
-
**
java.lang.Thread.interrupt():**中断目标线程,给目标线程发一个中断信号,线程被打上中断标记(设为true)。javapublic void interrupt() { if (this != Thread.currentThread()) { checkAccess(); // thread may be blocked in an I/O operation synchronized (interruptLock) { Interruptible b = nioBlocker; if (b != null) { interrupted = true; interrupt0(); // inform VM of interrupt b.interrupt(this); return; } } } // 打上中断标记 interrupted = true; // interrupt0()是一个native方法,主要就是用cpp从OS层面将线程唤醒 interrupt0(); // inform VM of interrupt } -
**
java.lang.Thread.isInterrupted():**判断目标线程是否被中断,不会清除中断标记。javapublic boolean isInterrupted() { return interrupted; } -
**
java.lang.Thread.interrupted():**判断目标线程是否被中断,会清除中断标记。javapublic static boolean interrupted() { return currentThread().getAndClearInterrupt(); } // ... boolean getAndClearInterrupt() { boolean oldValue = interrupted; // 因为读取interrupted字段时也可能被中断 // 此时如果直接清除interrupted,会导致这次新的中断丢失 // 所以这里要判断一下 if (oldValue) { interrupted = false; clearInterruptEvent(); // native方法 } return oldValue; }
中断标识interrupted
Thread中有一个中断标识属性volatile boolean interrupted ,用于标记当前线程是否中断。有两点要注意:
Thread.interrupt()方法做两个工作:- 在当前线程将中断标志修改为true,并不是停止线程
- 通过native的
interrupt0()将等待的线程唤醒
- 如果当前线程在调用Object类的
wait()方法或者这个类的join()或sleep()方法被打断时,会将它的中断状态将被清除(interrupted设为false),并且它会收到一个InterruptedException异常。
当线程正在等待、休眠或以其他方式被占用,并且线程在活动之前或期间被中断抛出,都会抛出
InterruptedException
-
(个人理解)抛出
InterruptedException,这个行为是中断的直接结果,捕获这个异常后我们可以让线程执行唤醒后的逻辑(继续运行或者return结束)javatry{ Thread.sleep(3000); } catch (InterruptedException e) { // 打印日志或者return退出都可以 // ... } // 当前线程的后续逻辑 // ... -
线程通过抛出IE从非活跃状态(WAITING、TIMED WAITING)恢复到活跃状态(RUNNABLE)。如果线程本身就是活跃状态,则会无视RUNNABLE,也不会抛出IE。此时如果想处理中断,就需要**
isInterrupted(),interrupted()**来获取中断状态标识interrupted
一些小练习
Thread.interrupt() 只唤醒线程并修改中断标识
sleep、wait、join挂起线程都会修改中断标识符并抛出InterruptedException,如果没有抛出IE的情况下(比如yield后线程还是RUNNABLE状态)希望响应中断,则需要isInterrupted(),interrupted()来获取中断状态标识interrupted
java
private static void test1(){
// 程序中没有响应中断信号的逻辑,线程不会被中断
Thread thread = new Thread(()->{
System.out.println("线程启动");
while (true){
Thread.yield();
}
});
thread.start();
thread.interrupt();
}
java
private static void test2(){
// 手动响应中断,退出线程
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程启动");
while (true){
Thread.yield();
// 响应中断
if (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+": 线程被中断");
return;
}
}
});
thread.start();
thread.interrupt();
}
sleep() 清除中断状态标识
java
private static void test3() throws InterruptedException {
// 中断失败,因为sleep会将当前线程的interrupted清楚(设为false)
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName()+": 线程启动");
while (true){
// 响应中断
if (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+": 线程被中断,程序退出");
return;
}
try{
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+": 线程休眠被中断");
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
- 最开始,子线程start,
Thread.currentThread().isInterrupted()为false,不能进入if(Thread.currentThread().isInterrupted())直接进入try里的sleep(3s) - 主线程sleep(2s)
- 2s后,主线程自己到点唤醒,通过
thread.interrupt()中断唤醒子线程,此时中断标识interrupted为true,由于sleep()抛出InterruptedException(同时将中断标识清除,设为false)被catch捕获,打印"线程休眠被中断" - 回到
if (Thread.currentThread().isInterrupted()),由于前面sleep抛出IE的同时将中断标识设为false,所以不能执行这个if里的逻辑,结果就是一直在while里重复sleep

java
private static void test4() throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+": 线程启动");
while (true){
// 响应中断
if (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+": 线程被中断,程序退出");
return;
}
try{
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+": 线程休眠被中断");
Thread.currentThread().interrupt();
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
这里再catch到sleep的IE之后再通过Thread.currentThread().interrupt() 把当前线程的interrupted 表示符置为true,下一个循环就能执行if (Thread.currentThread().isInterrupted()) 的return逻辑了。

为什么呢?
java
public static void main(String[] args) {
System.out.println(Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
try {
System.out.println(LocalTime.now() +": 开始睡眠");
System.out.println(Thread.currentThread().isInterrupted());
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().isInterrupted());
System.out.println(LocalTime.now()+": 发生中断");
}
System.out.println(LocalTime.now()+": 结束睡眠");
}

打印一下中断标识,可以看出sleep之前interrupt()已经将interrupted设为true,sleep会将其改为false并直接抛出IE
Reference
【Java并发·08】线程中断 interrupt
对于Java线程中断的理解,哪种情况下会响应中断?哪种情况下不响应中断?
Java 线程中断?看这篇就够了