一、原理介绍
1、使用interrupt来通知,而不是强制
停止线程的需求:用户取消、运行出错等。
在java中,我们只能告诉线程你应该中断啦,但是最终决定权还是在线程本身。
没有能力去做到强制停止。其实,想要停止线程就是用interrupt来通知那个线程,以及被通知的那个线程如何配合。这就是停止线程的一个核心,而不是强制停止。
二、最佳实践 正确的停止方法:interrupt
①通常线程会在什么情况下停止普通情况
package stopthreads;
/**
* 描述: run方法内没有sleep或wait方法时,停止线程
*/
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
while( !Thread.currentThread().isInterrupted()&&num <= Integer.MAX_VALUE / 2){//
if(num % 1000 == 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);//1秒后调用interrupt()
thread.interrupt();
}
}
在while语句添加isInterrupt()进行判断。
前后对比!确实调用interrpt()方法后线程停止运行。
②线程可能被阻塞
package stopthreads;
/**
* 描述: 带有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();
}
}
③如果线程在每次迭代(循环) 后都阻塞
package stopthreads;
/**
* 描述: 如果在执行过程中,每次循环都会调用sleep或wait等方法,
* 那么不需要每次迭代都检查是否已中断
*/
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = ()->{
int num = 0;
try {
while(num <= 10000 ){//判断isInterrupt()多余代码,因为我们会在中断的过程中检测到中断状态,并且抛出这个异常。
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();
}
}
④while内try/catch的问题
package stopthreads;
/**
* 描述: 如果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();
}
}
报错后继续运行。。。
因为java在设计Slee()函数时,当它一旦响应中断,便会立即把interrupt这个标记位给清除。所以当再次运行到while时,由于中断的效果已经被清除,检查不到任何被中断的迹象。
⑤实际开发中的两种最佳实践:1、传递中断 2、恢复中断 3、不应疲敝中断
1)优先选择:传递中断
package stopthreads;
/**
* 描述: 最佳实践:catch了InterruptedExcetion之后的优先选择:
* 在方法签名中抛出异常
* 那么在run()就会强制try/catch
*/
public class RightWayStopThreadInProd implements Runnable{
@Override
public void run() { //较高层次
while(true){
System.out.println("go");
throwInMethod();
}
}
private void throwInMethod() { //低层次 吞掉
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
通常抛出异常有两种方式,一种是在方法后面直接throw,另一种就是try/catch.
当用try/catch时,
在这个throwInMethod()方法,它是低层次的相对run()方法,在检测到异常时,抛出异常,这时这个异常就会被吞掉。
这时我们就得上报给更高一级的函数, 在方法后面添加抛出异常的语句。
@Override
public void run() { //较高层次
while(true){
System.out.println("go");
try {
throwInMethod();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
让run()方法(更高级的函数)去处理这个异常,就不会导致这个异常被吞掉。
2)不想或无法传递:恢复中断
恢复中断的方式也就是在catch子语句中调用Thread.currentThread().interrupt() 来恢复设置中断状态。
package stopthreads;
/**
* 描述: 最佳实践2:在catch子语句中调用Thread.currentThread().interrupt()
* 来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生的中断
*/
public class RightWayStopThreadInProd2 implements Runnable{
@Override
public void run() {
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interrupt,程序运行结束");
break;
}
System.out.println("go");
reInterrupt();
}
}
private void reInterrupt() {
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();
}
}
⑥响应中断的方法总结列表
Object.wait()、Thread.sleep()、Thread.join()、java.util.concurrent.BlockingQueue.take()/put()、
locks.Lock.lockInterruptibly()
三、错误的停止方法
①被弃用的stop,suspend和resume方法
②用volatile设置boolean标记位
四、停止线程的相关函数解析
五、常见面试题
、