一、原理
使用interrupt来通知线程停止,而不是强制停止
Java中最多也就是告诉线程该停止了,而线程本身有最高决定权,是否停止,何时响应停止
二、正确的停止方法(interrupt)
1、普通情况下线程停止
- 使用其他线程通知想要停止的线程停止
- 在应该停止的线程里要有响应停止代码,否则也不会停止,使用Thread.currentThread().isInterrupted()来检查是否停止
/**
* run方法内没有sleep或wait方法时,停止线程
*
* @author wjh
* @date 2020-03-04 14:01
*/
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
// Thread.currentThread().isInterrupted()检查当前线程是否中断
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是10000的倍数");
}
num++;
}
System.out.println("任务运行结束");
}
public static void main(String[] args) {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通知线程停止
thread.interrupt();
}
}
运行结果:
1、不通知停止
2、通知停止,比正常结束提前了很多
2、在线程阻塞的情况下停止线程
如果线程处于阻塞状态,该线程响应停止的方法是抛出一个异常
/**
* 带有sleep的中断线程的写法
*
* @author wjh
* @date 2020-03-04 14:24
*/
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (!Thread.currentThread().isInterrupted() && num <= 300) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
结果:
3、如果线程在每次迭代后都阻塞
如果执行过程中,每次循环都会调用sleep或者wait等待,则不需要每一次检查都检查是否以中断
/**
* 如果执行过程中,每次循环都会调用sleep或者wait等待,则不需要每一次检查都检查是否以中断
*
* @author wjh
* @date 2020-03-04 14:36
*/
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();
}
}
结果:
如果在while内使用try/catch,会使中断无效
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();
}
}
结果:
三、最佳实践
1、优先选择:传递中断
/**
* 最佳实践:catch了InterruptedException之后的优先选择
* 在方法签名中抛出异常
* 这样在run方法中就会强制try/catch
*
* @author wjh
* @date 2020-03-04 14:53
*/
public class RightWayStopThreadInProd implements Runnable {
@Override
public void run() {
while (true) {
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、不想或无法传递:回复中断
/**
* 最佳实践2:在catch中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行过程中,依然能够检查到刚才发生的中断
*
* @author wjh
* @date 2020-03-04 14:53
*/
public class RightWayStopThreadInProd2 implements Runnable {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted程序运行结束");
break;
}
System.out.println("go");
reInMethod();
}
}
private void reInMethod() {
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();
}
}
结果
四、能够响应中断的方法
五、错误的停止方法
1、被弃用的stop、suspend和resume方法
2、用volatile设置boolean标记位