李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
06.并发编程之interrupt方法详解
Leefs
2022-10-08 PM
1347℃
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
NLP
4
标签云
pytorch
JVM
SpringBoot
正则表达式
Filter
队列
CentOS
Hive
Stream流
Map
Git
MyBatisX
人工智能
递归
Beego
算法
随笔
SQL练习题
排序
GET和POST
Netty
字符串
Redis
锁
Nacos
MySQL
FastDFS
Elasticsearch
线程池
链表
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭