多线程探险——2如何正确的中断java线程
一、内容安排
- 什么是守护线程(Daemon Thread)
- Join方法的含义
- 自己join自己会是什么效果
- Interrupt方法
- 综合案列:如何去停止线程
二、文章内容
为了能中断线程我们首先需要了解线程中的几个方法, 最后在案列中实现如何正确的停止一个线程; 如果对这几个方法非常熟悉可跳过前面内容直接看综合案列
1. 什么是守护线程(Daemon Thread)
-
守护线程:从字面意思我们可以感觉到好像是要守护某个事物一样, 实际上简单理解守护线程就是另外一个线程的兄弟(真的做到了不求同年同月同日生,但求同年同月同日死的那种兄弟), 到代码中就是主线程生命周期结束守护线程申明周期跟着结束。
-
应用场景:心跳检测的场景, 当主线程和另外一台服务器保持长连接的通信时需要定时检测心跳,那么假如主线程停止了心跳检测线程同样应该结束申明周期
-
代码实现
- 目标:此处模拟主线程(mian线程)工作10秒, 守护线程是死循环不停的工作
- 效果:十秒之后主线程生命周期结束,守护线程也随之停止工作
-
具体代码
public class DaemonThreadDemo { public static void main(String[] args) throws InterruptedException { //1.创建一个守护线程 Thread daemonThread = new Thread(() -> { while (true) { System.out.println("daemon thread is working..."); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }); //2.设置daemonThread作为mian的守护线程 daemonThread.setDaemon(true); //3.启动守护线程 daemonThread.start(); //4.模拟主线程工作10秒 TimeUnit.SECONDS.sleep(10); } }
-
思考: 如果我不设置daemonThread为面的守护线程他会不会随着main的结束而结束
2.Join方法的含义
-
join:join方法字面意思就是加入, 通俗的理解join方法就是多个线程是一个饭点组合队伍, 有个兄弟A在洗头他喊了你一声等我一下我马上洗完了一起去,这时候你就只能等着他了; 在代码中如果A线程join到B线程那么B线程就会一直等着A线程执行结束后才会执行join后面的代码
-
应用场景:
- 一个主线程开了三个线程去执行任务, 当三个线程执行完成任务后主线程才能继续执行后续代码
-
案列步骤
- 实现在主线程(你自己)中开启一个兄弟线程A
- 在A中实现洗头十分钟
- 在主线程中启动A线程, 然后调用A的join方法
- 在join之后打印兄弟你终于整完了
-
案列实现
public class JoinDemo { public static void main(String[] args) throws InterruptedException { //1.A兄弟线程 Thread A = new Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println("A兄弟在洗头中"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }); //2.A兄弟开始洗头 A.start(); //3.A兄弟叫Mian兄弟等她 A.join(); //4.A洗完头了 System.out.println("兄弟你终于洗完头了, 走吧去哪里吃饭!!!"); } }
3.自己join自己会是什么效果
-
情景: 假如你当前线程调用了join方法这会有什么效果, 也就是说自己等自己,实际上这是一个死循环程序一直也不会结束;
-
如何获取当前线程
Thread yourself = Thread.currentThread();
-
具体代码
public class JoinYourself { public static void main(String[] args) throws InterruptedException { Thread yourself = Thread.currentThread(); yourself.join(); } }
-
最终效果
- 程序一直不会结束,处于阻塞状态
- 线程快照如下:
"main" #1 prio=5 os_prio=0 tid=0x0000000002b13800 nid=0x40f8 in Object.wait() [0x0000000000dcf000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076b105ca0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1249) - locked <0x000000076b105ca0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1323) at com.huangguoyu.JoinYourself.main(JoinYourself.java:6)
4.Interrupt方法
-
Interrupt方法:
- 该方法可以改变线程是否被中断的一个状态值,但是他不会真正的中断线程; 可以通过isInterrupted方法获取这个状态值的改变
- 调用该方法后如果线程整处于join、sleep、wait状态那么线程会捕获到InterruptedException异常
-
尝试使用Interrupt中断线程
- 实际效果是不能终止线程, 程序会一直处于运行状态
public class InterruptedMethod { public static void main(String[] args) { Thread thread = new Thread(() -> { while (true) { } }); thread.start(); thread.interrupt(); System.out.println(thread.isInterrupted()); } }
-
测试线程在sleep状态能不能捕获到InterruptedException异常
public class InterruptedMethod { public static void main(String[] args) { Thread thread = new Thread(() -> { try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { System.out.println("捕获到了异常!!!"); e.printStackTrace(); } }); thread.start(); thread.interrupt(); System.out.println(thread.isInterrupted()); } }
5.如何停止线程
- 线程的stop方法, 此方法被标记了将被抛弃,此处不再详细介绍
- 可以使用状态量flag来停止线程(这里实现方式涉及到java内存模型后续更新)
- 首先停止线程我们可以设置线程为某个线程的守护线程只要主线程申明周期结束了守护线程也随之结束了; 那么问题来了我们怎么来控制主线程,实际上我们可以让主线程处于wait、join、sleep状态我们就可以调用主线程的中断方法来结束主线程;那么挖掘机又来了, 主线程执行完守护先成就结束,假如这时候守护线程任务还没有执行完怎么, 好的大家可能想到了join可以让主线程等某个兄弟
-
案列:
实现一个线程去执行任务,并且在这个线程中提供中断任务的方法,也就是提供停止执行任务的方法
- 自定义线程类,并提供execute方法用于执行任务
- 在run方法中开启一个任务线程去执行任务
- 在自定义线程类中提供shutdown方法去停止当前线程
- 测试
-
案列实现
public class MyIterruptedableThread extends Thread{ //需要执行的任务 private Runnable runnable; public MyIterruptedableThread(Runnable target) { this.runnable = target; } @Override public void run() { //开启执行任务线程 Thread task = new Thread(runnable); //设置task线程为当前线程的守护线程 task.setDaemon(true); task.start(); //让当前线程等待task线程执行结束 try { task.join(); } catch (InterruptedException e) { //接收到中断异常当前线程执行结束, 其守护线程task也随之结束 e.printStackTrace(); } } /** * 中断当前线程 */ public void shutDown() { this.interrupt(); } /** * 测试 */ public static void main(String[] args) throws InterruptedException { //定义任务 Runnable task = ()->{ while (true) { System.out.println("task is processing..."); } }; //创建主线,控制任务生命周期 MyIterruptedableThread myIterruptedableThread = new MyIterruptedableThread(task); //启动线程执行任务 myIterruptedableThread.start(); //执行5秒后中断任务 TimeUnit.SECONDS.sleep(5); System.out.println("任务执行五秒, 结束任务"); myIterruptedableThread.shutDown(); } }