interrupt我们首先来简单介绍一下interrupt方法:
- 翻译过来就是打断,本质是将线程的打断标记设为true,并调用线程的三个parker对象(C++实现级别)unpark该线程 。
- 打断操作实际上只是通知,不是打断
- 打断线程不等于中断线程,有以下两种情况:
- 打断正在运行中的线程并不会影响线程的运行,但如果线程监测到了打断标记为true , 可以自行决定后续处理 。
- 打断阻塞中的线程会让此线程产生一个InterruptedException异常 , 结束线程的运行 。
- 但如果该异常被线程捕获住 , 该线程依然可以自行决定后续处理(终止运行 , 继续运行,做一些善后工作等等)
/* 正常运行情况下打断,并不会影响线程正常运行 , 但是会将线程的打断标记设置为true */package cn.itcast.test;import lombok.extern.slf4j.Slf4j;@Slf4j(topic = "c.Test12")public class Test12 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {// 这里就是一个正常运行的线程 , 我们在启动后1s对他进行打断while(true) {// 打断不会直接对线程造成影响 , 但是会将打断状态interrupted变为true(由isInterrupted方法得到)boolean interrupted = Thread.currentThread().isInterrupted();// 然后我们可以自行根据打断状态来做对应处理!if(interrupted) {log.debug("被打断了, 退出循环");break;}}}, "t1");t1.start();// 1s后实现打断操作Thread.sleep(1000);log.debug("interrupt");t1.interrupt();}}// 产生结果为:程序不停止 , 但打断状态为true20:57:37.964 [t2] c.TestInterrupt - 打断状态: true/* 不正常状态被打断,例如sleep,yield,join,会使线程进入阻塞状态,抛出异常,会清空打断状态,使其变为false */private static void test1() throws InterruptedException {Thread t1 = new Thread(()->{sleep(1);}, "t1");t1.start();sleep(0.5);t1.interrupt();log.debug(" 打断状态: {}", t1.isInterrupted());}// 产生结果为:抛出异常,但程序不会停止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 cn.itcast.n2.util.Sleeper.sleep(Sleeper.java:8) at cn.itcast.n4.TestInterrupt.lambda$test1$3(TestInterrupt.java:59) at java.lang.Thread.run(Thread.java:745)21:18:10.374 [main] c.TestInterrupt - 打断状态: false
最后我们介绍一个用于interrupt的思想模式之两阶段终止:- 我们需要在线程1调用线程2的interrupt方法,同时使线程2完成它的后续操作

文章插图
我们通过代码解释:
/*首先利用isinterrupt方法来实现思想*/// 我们需要书写一个检测类负责不断检测打断状态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等操作就会导致抛出异常InterruptedException// 但是抛出异常并不会导致程序结束,也不会导致打断标记为true,// 所以我们需要手动设置打断标记为true,使其在下一次循环时,中断程序current.interrupt();}// 执行监控操作}},"监控线程");thread.start();}public void stop() {thread.interrupt();}}// 我们在主程序调用:TPTInterrupt t = new TPTInterrupt();t.start();Thread.sleep(3500);log.debug("stop");t.stop();// 我们可以得到结果:11:49:42.915 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:43.919 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:44.919 c.TwoPhaseTermination [监控线程] - 将结果保存11:49:45.413 c.TestTwoPhaseTermination [main] - stop11:49:45.413 c.TwoPhaseTermination [监控线程] - 料理后事/*我们还可以手动设置一个参数来负责线程的关闭*/// 停止标记用 volatile 是为了保证该变量在多个线程之间的可见性// 我们的例子中,即主线程把它修改为 true 对 t1 线程可见class TPTVolatile {private Thread thread;private volatile boolean stop = false;public void start(){thread = new Thread(() -> {while(true) {Thread current = Thread.currentThread();if(stop) {log.debug("料理后事");break;}try {Thread.sleep(1000);log.debug("将结果保存");} catch (InterruptedException e) {}// 执行监控操作}},"监控线程");thread.start();}public void stop() {stop = true;thread.interrupt();}}// 我们在主程序中调用:TPTVolatile t = new TPTVolatile();t.start();Thread.sleep(3500);log.debug("stop");t.stop();// 我们得到下属结果:11:54:52.003 c.TPTVolatile [监控线程] - 将结果保存11:54:53.006 c.TPTVolatile [监控线程] - 将结果保存11:54:54.007 c.TPTVolatile [监控线程] - 将结果保存11:54:54.502 c.TestTwoPhaseTermination [main] - stop11:54:54.502 c.TPTVolatile [监控线程] - 料理后事/*我们还可以通过打断 park 线程来实现思想*/// 首先我们简单介绍一下park:当打断标记为false时,park起到线程暂停作用;当打断标记为true时,继续运行// 首先我们给出打断标记为false的状态private static void test3() throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("park...");LockSupport.park();log.debug("unpark...");log.debug("打断状态:{}", Thread.currentThread().isInterrupted());}, "t1");t1.start();sleep(0.5);t1.interrupt();}// 我们可以得到下述结果21:11:52.795 [t1] c.TestInterrupt - park...21:11:53.295 [t1] c.TestInterrupt - unpark...21:11:53.295 [t1] c.TestInterrupt - 打断状态:true // 如果打断标记已经是 true, 则 park 会失效private static void test4() {Thread t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {log.debug("park...");LockSupport.park();log.debug("打断状态:{}", Thread.currentThread().isInterrupted());}});t1.start();sleep(1);t1.interrupt();}// 我们可以得到下述结果:21:13:48.783 [Thread-0] c.TestInterrupt - park...21:13:49.809 [Thread-0] c.TestInterrupt - 打断状态:true21:13:49.812 [Thread-0] c.TestInterrupt - park...21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true21:13:49.813 [Thread-0] c.TestInterrupt - park...21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true21:13:49.813 [Thread-0] c.TestInterrupt - park...21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true21:13:49.813 [Thread-0] c.TestInterrupt - park...21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true // 我们可以使用 Thread.interrupted() 清除打断状态// Thread.interrupted()获得当前打断状态 , 并将打断状态设置为false// Thread.currentThread().isInterrupted())只能获得当前打断状态,不会影响值/*此外还有一些不符合当前状态的打断方式,我们也简单介绍一下:- 使用线程对象的 stop() 方法停止线程- stop 方法会真正杀死线程 , 如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁- 使用 System.exit(int) 方法停止线程- 目的仅是停止一个线程,但这种做法会让整个程序都停止*/
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Seata 1.5.2 源码学习
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记
- Helm干货!速度围观!
- .NET 源码学习 [数据结构-线性表1.2] 链表与 LinkedList<T>
- MPC:百万富翁问题
- PGL Paddle Graph Learning 关于图计算&图学习的基础知识概览:前置知识点学习
- JVM学习笔记——内存模型篇
- Linux学习环境搭建流程
- Archlinux + Dwm 配置流程
- 关于入门深度学习mnist数据集前向计算的记录