3.线程的八大核心基础知识之如何正确停止线程
Posted zhihaospace
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3.线程的八大核心基础知识之如何正确停止线程相关的知识,希望对你有一定的参考价值。
一.概述
二.原理介绍
-
使用interrupt来通知,而不是强制
解释:由于我们无法强行停止线程,只是告诉线程需要停止了,线程才可能进行收尾清理最后停止,也可能停止不了。控制权不在我们手中。
三.最佳实践:如何正确停止线程
1.正确的停止方法:interrupt
(1)普通情况下如何停止线程
代码一:使用interrupt通知子线程,但子线程并不停止
/** * run方法内没有sleep或wait方法时,停止线程 */ public class RightWayStopThreadWithoutSleep implements Runnable{ @Override public void run() { int num = 0; while(num <= Integer.MAX_VALUE / 2){ if (num % 10000 == 0){ System.out.println(num+"是10000的倍数"); } num++; } System.out.println("任务运行结束"); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadWithoutSleep()); //在主线程中开启子线程,然后主线程休眠1s后通知子线程中断 thread.start(); Thread.sleep(1000); thread.interrupt(); } }
方法二:在子线程中接收中断通知,并处理中断
-
修改while语句 while(!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2)
(2)在线程阻塞情况下如何停止线程(非阻塞循环)
-
子线程使用sleep睡眠1s时,主线程在睡眠0.5s后通知正在睡眠的子线程中断,会发生异常
-
加上 && !Thread.currentThread().isInterrupted() 是为了防止在非阻塞的while循环中出现中断
-
在线程阻塞的情况下,主线程使用interrupt通知子线程中断然后在子线程中判断是否被中断最后使用try...catch捕获处理即可(try...catch在while外面)
/** * run方法内带有sleep或wait方法时,停止线程 */ public class RightWayStopThreadWithSleep{ public static void main(String[] args) throws InterruptedException { Runnable runnable = ()->{ int num = 0; try { while (num <= 300 && !Thread.currentThread().isInterrupted()){ if (num % 100 == 0){ System.out.println(num + "是100的倍数"); } num++; } Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); Thread.sleep(500); thread.interrupt(); } }
(3)如果线程在每次迭代后都阻塞(阻塞循环)
-
每次循环中都有一段时间阻塞,所以在阻塞循环中我们不需要 && !Thread.currentThread().isInterrupted() ,因为每次都是在Thread.sleep(10)中出现中断
-
在线程迭代阻塞的情况下,主线程使用interrupt通知子线程中断然后在子线程使用try...catch捕获处理即可(try...catch在while外面)
/** * run方法内每次循环都会带有sleep或wait方法时,停止线程 */ public class RightWayStopThreadWithSleepEveryLoop { public static void main(String[] args) throws InterruptedException { Runnable runnable = ()->{ int num = 0; try { while (num <= 10000){ if (num % 100 == 0){ System.out.println(num + "是100的倍数"); } num++; Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); Thread.sleep(5000); thread.interrupt(); } }
(4)while内try/catch的问题
-
由于sleep函数响应中断之后会把interrupt标记位清除,所以一直在while循环中执行
public class CantInterrupt { public static void main(String[] args) throws InterruptedException { Runnable runnable = () -> { int num = 0; while (num <= 10000) { if (num % 100 == 0) { System.out.println(num + "是100的倍数"); } num++; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(runnable); thread.start(); Thread.sleep(5000); thread.interrupt(); } }
修改while循环 while (num <= 10000 && !Thread.currentThread().isInterrupted()) ,结果一样没有区别
(5)实际开发中的两种最佳实践
-
优先选择:传递中断
-
不想或无法传递:恢复中断
-
不应该屏蔽中断,要使用上面两种最佳实践
【1】优先传递中断
-
在run方法中调用的函数,需要将异常传递给run方法处理而不是自己处理
-
自己处理就会出现中断被清除,run方法中的循环停止不了
/** * 最佳实践:catch了InterruptedException之后优先选择:在方法签名中抛出异常 * 最佳做法:必须在run中try/catch来处理异常而不是在run的内部调用方法try/catch, * 并且run方法无法抛出异常 */ public class RightWayStopThreadInProd implements Runnable { @Override public void run() { while (true && !Thread.currentThread().isInterrupted()) { System.out.println("go"); try { throwInMethod(); } catch (InterruptedException e) { System.out.println("保存日志"); e.printStackTrace(); } } } private void throwInMethod() throws InterruptedException { Thread.sleep(2000); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadInProd()); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
【2】不想或无法传递中断时就恢复中断
-
在方法中添加Thread.currentThread().interrupt()用来传递给run方法中断
/** * 最佳实践:被调用的方法中在catch语句中调用Thread.currentThread().interrupt来恢复中断 */ public class RightWayStopThreadInProd2 implements Runnable { @Override public void run() { while (true) { if (!Thread.currentThread().isInterrupted()){ System.out.println("Interrupted,程序运行结束"); break; } throwInMethod(); } } private void throwInMethod() { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new RightWayStopThreadInProd2()); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
(6)响应中断的方法总结列表
2.正确停止带来的好处
-
目的是线程安全并且数据完整性得到了保障
3.补充内容:Java异常体系
-
Error:出现严重错误,捕获了也没用已经处理不了了
-
RuntimeException:unchecked exception(不受检查异常),程序不可预知是由于程序员在写程序的时候出现的错误,需要修改程序防止RuntimeException
-
其他的Exception:checked exception(受检查异常),程序可预知的异常,必须处理
四.错误的停止线程的方法
1.被弃用的stop,suspend和resume方法
-
stop会立即停止,对于像银行转账业务就会造成巨大影响
-
suspend和resume会带着锁休息这可能会造成死锁
2.用volatile设置boolean标记位
(1)看上去可行
(2)错误原因
(3)修正方案
五.重要函数的源码解析
六.彩蛋
七.常见面试问题
以上是关于3.线程的八大核心基础知识之如何正确停止线程的主要内容,如果未能解决你的问题,请参考以下文章