一、原理介绍:java
使用interrupt来通知,而不是强制。安全
在Java中,最好的中止线程的方式是使用中断 Interrupt,可是这仅仅是会通知到被终止的线程“你该中止运行了”,被终止的线程自身拥有决定权(决定是否、以及什么时候中止),这依赖于请求中止方和被中止方都遵照一种约定好的编码规范。数据结构
任务和线程的启动很容易。在大多数时候,咱们都会让它们运行直到结東,或者让它们自行中止。然而有时候咱们但愿提早结東任务或线程或许是由于用户取消了操做,或者服务要被快速关闭,或者是运行超时或出错了。要使任务和线程能安全、快速、可靠地中止下来,并非一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断( Interruption这是一种协做机制,可以使一个线程终止另外一个线程的当前工做)。ide
这种协做式的方法是必要的,咱们不多但愿某个任务、线程或服务当即中止,由于这种当即中止会使共享的数据结构处于不一致的状态。相反,在编写任务和服务时可使用一种协做的方式:当须要中止时,它们首先会清除当前正在执行的工做,而后再结束。这提供了更好的灵活性,由于任务自己的代码比发出取消请求的代码更清楚如何执行清除工做。函数
生命周期结束(End-of-Lifecycle)的问题会使任务、服务以及程序的设计和实现等过程变得复杂而这个在程序设计中很是重要的要素却常常被忽略。一个在行为良好的软件与勉强运的软件之间的最主要区别就是,行为良好的软件能很完善地处理失败、关闭和取消等过程。oop
二、如何正确中止线程编码
三、使用interrupt中止的几种状况。spa
/** * @data 2019/11/9 - 下午8:07 * 描述:run方法内没有sleep或wait方法时,中止线程 */ public class RightWayStopThreadWithoutSleep implements Runnable{ @Override public void run() { int num = 0; while(num<=Integer.MAX_VALUE/2 && !Thread.currentThread().isInterrupted()){ 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()); thread.start(); thread.sleep(1000); thread.interrupt(); } }
/** * @data 2019/11/9 - 下午8:07 * 描述:带有sleep的中断线程的方法 */ 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(); } }
结果:
0是100的倍数 100是100的倍数 200是100的倍数 300是100的倍数 java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at threadcoreknowledge.stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:18) at java.base/java.lang.Thread.run(Thread.java:844)
sleep、wait等一些方法使线程阻塞,当线程收到通知interrupt时候,这些方法处理该通知的方法是抛出InterruptedException异常。线程
/** * @data 2019/11/10 - 上午9:13 * 描述:若是每次执行过程当中,每次循环中都调用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的倍数"); Thread.sleep(10); num++; } } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.start(); Thread.sleep(5000); thread.interrupt(); } }
在循环过程当中,cpu运行速度快,大部分时间都熬在使其阻塞的方法中,因此不必每次迭代检查是否已中断-(Thread.currentThread().isInterrupted())设计
四、若是while里面放try/catch,会致使中断失效
/**
* @data 2019/11/10 - 上午9:24
* 描述:若是while里面放try/catch,会致使中断失效
*/
public class CantInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = ()->{
int num = 0;
while(num<10000&& !Thread.currentThread().isInterrupted()){
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();
}
}
结果:
0是100的倍数 100是100的倍数 200是100的倍数 300是100的倍数 400是100的倍数 java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at threadcoreknowledge.stopthreads.CantInterrupt.lambda$main$0(CantInterrupt.java:17) at java.base/java.lang.Thread.run(Thread.java:844) 500是100的倍数 600是100的倍数 700是100的倍数
即便加了检查是否已中断,但程序抛出了异常后while里面的内容依然执行,这是由于当sleep、wait等函数阻塞线程后,会将该线程的阻塞标志清除,这就致使即便通知终端信号给线程了,线程检测不出
五、实际开发中处理终端的最佳方法:
/** * @data 2019/11/10 - 上午9:41 * 描述:最佳实践:catch了InterruptedExcetion只有的优先选择:在方法签名中抛出异常 * 那么在run()就会强制try/catch */ public class RightWayStopTHreadInProd implements Runnable{ @Override public void run() { while (true){ try { System.out.println("go"); throwInMethod(); } catch (InterruptedException e) { 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(); } }
缘由:
优先选择在方法上抛出异常。
用 throws Interruptedexception标记你的方法,不采用ty语句块捕获异常,以便于该异常能够传递到顶层,让run方法能够捕获这一异常,例如:
因为run方法内没法抛出 checked Exception(只能用 try catch),顶层方法必须处理该异常,遵免了漏掉或者被吞掉的状况,加强了代码的健壮性。