李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
06.并发编程之interrupt方法详解
Leefs
2022-10-08 PM
745℃
0条
[TOC] ### 一、概述 中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地**中止**其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。 ### 二、中断线程方法 #### interrupt() **描述**:该方法用于中断Thread线程,此线程并非当前线程,而是调用interrupt()方法的实例所代表的线程,并不是强制关闭线程,而是将中断标记位设置为true,线程的中断需要在线程内部协作关闭 ##### 使用示例 + 通过interrupt()中断正在休眠的线程 ```java /** * @author lilinchao * @date 2022-10-08 * @description 通过interrupt() + InterruptedException来中断线程 **/ @Slf4j(topic = "c.Test02") public class Test02 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("start..."); //线程进入睡眠状态 sleep 5s Sleeper.sleep(5); log.debug("end..."); } }; t1.start(); //主线程睡眠 2s 后运行 t1.interrupt 打断正在睡眠的t1线程 Sleeper.sleep(2); //在线程阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态 t1.interrupt(); } } ``` **运行结果** ``` 14:21:10.443 c.Test02 [t1] - start... java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.lilinchao.thread.utils.Sleeper.sleep(Sleeper.java:8) at com.lilinchao.thread.demo01.Test02$1.run(Test02.java:19) 14:21:12.457 c.Test02 [t1] - end... ``` **说明** + 打断正在运行中的线程并不会影响线程的运行,但如果线程监测到了打断标记为true,可以自行决定后续处理。 + 打断阻塞中的线程(如:线程被`Object.wait`, `Thread.join`和`Thread.sleep`三种方法之一阻塞)会让此线程产生一个`InterruptedException`异常,结束线程的运行。但如果该异常被线程捕获住,该线程依然可以自行决定后续处理(终止运行,继续运行,做一些善后工作等等) + 如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 `InterruptedException`。 #### isInterrupted() **描述**:用于判断thread的中断状态,不清除中断状态 ##### 使用示例 + 停止正在运行中的线程 ```java /** * @author lilinchao * @date 2022-10-08 * @description 中断正常运行的线程 **/ @Slf4j(topic = "c.Test09") public class Test09 { public static void main(String[] args) { Thread t1 = new Thread(() -> { //无限循环 while (true){ Thread current = Thread.currentThread(); //判断当前线程的中断状态 boolean interrupted = current.isInterrupted(); //如果为true表示线程被中断,执行判断条件,通过break语句退出循环 if (interrupted) { log.debug("打断状态:{}",interrupted); break; } } },"t1"); t1.start(); Sleeper.sleep(1); t1.interrupt(); } } ``` **运行结果** ``` 14:43:19.316 c.Test09 [t1] - 打断状态:true ``` **说明** + 在while()循环中,每次循环,都会通过`isInterrupted()`方法判断当前线程的中断状态,如果为true表示线程被`interrupt()`方法中断,执行判断条件,通过break语句退出循环。以此达到终止正在运行线程的目的。 + 这样做的好处是,即使线程被中断,也不会立即停止该线程的运行,而是可以继续进行后续的善后操作,做到了”优雅的关闭线程“。 #### interrupted() **描述**:该方法为静态方法,判断当前线程的中断状态,并会将中断标记位设置为false,在第二次调用时中断状态会返回false ##### 使用示例 + 通过interrupted()方法停止正在运行中的线程 ```java /** * @author lilinchao * @date 2022-10-08 * @description 中断正常运行的线程 **/ @Slf4j(topic = "c.Test10") public class Test10 { public static void main(String[] args) { Thread t1 = new Thread(() -> { //无限循环 while (true){ Thread current = Thread.currentThread(); //判断当前线程的中断状态 boolean interrupted = current.interrupted(); //如果为true表示线程被中断,执行判断条件,通过break语句退出循环 if (interrupted) { log.debug("打断状态1:{}",interrupted); //再次调用interrupted方法,查看状态标记是否被重置 log.debug("打断状态2:{}",current.interrupted()); break; } } },"t1"); t1.start(); Sleeper.sleep(1); t1.interrupt(); } } ``` **运行结果** ``` 15:11:51.099 c.Test10 [t1] - 打断状态1:true 15:11:51.101 c.Test10 [t1] - 打断状态2:false ``` **说明** + interrupted()方法返回的是上一次的中断状态,并且会清除该状态,所以连续调用两次,第一次返回true,第二次返回false。 ### 三、两阶段终止模式 Two Phase Termination 在一个线程 T1 中如何“优雅”终止线程 T2? 这里的【优雅】指的是给 T2 一个料理后事的机会。 ![06.并发编程之interrupt方法详解01.jpg](https://lilinchao.com/usr/uploads/2022/10/71133894.jpg) **说明** + 如果线程在睡眠`sleep`期间被打断,打断标记是不会变的,为false,但是会抛出异常,此时需要手动设置打断标记为`true`,阻止线程继续运行; + 如果是在程序正常运行期间被打断的,那么打断标记就被自动设置为`true`。 + 处理好这两种情况就可以放心地来料理后事了 #### 错误思路 - 使用线程对象的 stop() 方法停止线程 - stop方法虽然可以强行终止正在运行或挂起的线程,但使用stop方法是很危险的。**stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁, 其它线程将永远无法获取锁。** - 使用 `System.exit(int)` 方法停止线程 - 目的仅是停止一个线程,但这种做法会让整个程序都停止 #### 使用 isInterrupted interrupt 可以打断正在执行的线程,无论这个线程是在 sleep,wait,还是正常运行 ```java import lombok.extern.slf4j.Slf4j; @Slf4j(topic = "c.Test12") public class Test12 { public static void main(String[] args) throws InterruptedException { TPTInterrupt t = new TPTInterrupt(); t.start(); //异常打断 Thread.sleep(3500); //正常打断 // Thread.sleep(1000); log.debug("stop"); t.stop(); } } @Slf4j(topic = "c.TPTInterrupt") class TPTInterrupt{ private Thread thread; // 启动监控线程 public void start(){ thread = new Thread(() -> { while(true) { // 获取当前线程打断状态 Thread current = Thread.currentThread(); // 判断是否打断 if(current.isInterrupted()) { log.debug("料理后事"); break; } try { Thread.sleep(1000); // 情况一 异常打断 log.debug("将结果保存"); // 情况二 正常打断 } catch (InterruptedException e) { // sleep 打断后会清除打断标记,所以要重新设置 current.interrupt(); e.printStackTrace(); } // 执行监控操作 } },"监控线程"); thread.start(); } public void stop() { thread.interrupt(); } } ``` **运行结果** ``` 17:45:27.063 c.TPTInterrupt [监控线程] - 将结果保存 17:45:28.076 c.TPTInterrupt [监控线程] - 将结果保存 17:45:29.089 c.TPTInterrupt [监控线程] - 将结果保存 17:45:29.557 c.Test12 [main] - stop java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.lilinchao.thread.demo01.TPTInterrupt.lambda$start$0(Test12.java:35) at java.lang.Thread.run(Thread.java:748) 17:45:29.558 c.TPTInterrupt [监控线程] - 料理后事 ``` #### 使用停止标记 ```java import lombok.extern.slf4j.Slf4j; @Slf4j(topic = "c.Test11") public class Test11 { public static void main(String[] args) throws InterruptedException { TwoPhaseTermination tpt = new TwoPhaseTermination(); tpt.start(); tpt.start(); tpt.start(); Thread.sleep(3500); log.debug("停止监控"); tpt.stop(); } } @Slf4j(topic = "c.TwoPhaseTermination") class TwoPhaseTermination{ // 监控线程 private Thread monitorThread; // 停止标记 private volatile boolean stop = false; // 判断是否执行过 start 方法 private boolean starting = false; // 启动监控线程 public void start(){ synchronized (this) { if (starting) { // false return; } starting = true; } monitorThread = new Thread(() -> { while (true) { // Thread current = Thread.currentThread(); // 是否被打断 if (stop) { log.debug("料理后事"); break; } try { Thread.sleep(1000); log.debug("执行监控记录"); } catch (InterruptedException e) { } } }, "monitor"); monitorThread.start(); } // 停止监控线程 public void stop() { stop = true; monitorThread.interrupt(); } } ``` **运行结果** ``` 17:47:34.749 c.TwoPhaseTermination [monitor] - 执行监控记录 17:47:35.757 c.TwoPhaseTermination [monitor] - 执行监控记录 17:47:36.769 c.TwoPhaseTermination [monitor] - 执行监控记录 17:47:37.239 c.Test11 [main] - 停止监控 17:47:37.239 c.TwoPhaseTermination [monitor] - 料理后事 ``` **说明** + 停止标记用 volatile 是为了保证该变量在多个线程之间的可见性 ### 四、不推荐的方法 有一些不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁 | 方法名 | static | 功能说明 | | :-------: | :----: | :------------------: | | stop() | | 停止线程运行 | | suspend() | | 挂起(暂停)线程运行 | | resume() | | 恢复线程运行 |
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2463.html
上一篇
05.并发编程之线程常见方法
下一篇
07.并发编程之守护线程
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
标签云
二叉树
BurpSuite
容器深入研究
LeetCode刷题
Git
Spark
Spark RDD
SpringBoot
GET和POST
算法
Java
ajax
Livy
nginx
前端
JavaScript
Golang基础
并发线程
MySQL
Kafka
Flume
递归
Java工具类
Python
栈
Netty
数据结构和算法
Zookeeper
NIO
Hbase
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞