三、线程的停止
1、如何正确停止线程?
使用interrupt来通知,而不是强制。这是一种规范,可以响应interrupt的线程就可以被中断,而有些不响应的线程则不受我们的控制而无法中断,因为被停止的线程比希望其停止的调用方更清楚何时停止/中断。
(1)通常线程会在什么情况下停止普通情况?
普通情况就是指runable状态,非阻塞,在程序中也是让它一直处于相加的状态,在几秒内无法运行结束。
public class ThreadsAbout {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
try {
int num = 0;
while (num <= 300) {
if (!Thread.currentThread().isInterrupted() && 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中300次自加很快,所以主线程在500ms让其中断时,thread应该处于
//Thread.sleep(1000);的阻塞状态中
Thread.sleep(500);
thread.interrupt();
}
}
子线程catch住了这个中断。

(3)如果线程在每次迭代后都阻塞?
每次循环中都有sleep或wait等情况,那么不需要每次迭代都检查是否已中断。
public class ThreadsAbout {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
try {
int num = 0;
while (num <= 10000) {
//这里少了中断监视
if (num % 10 == 0) {
System.out.println(num + "是10的倍数.");
}
num++;
Thread.sleep(10);
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
注意到if (num % 10 == 0)没有!Thread.currentThread().isInterrupted()的判断,因为中断都被catch住了.
while内try/catch的问题
/**
* 描述: 如果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();
}
}
结果是这样的

sleep在设计中,在catch住了中断后会把中断标志位给清除,所以后面是没有检测到线程中断的。
2、开发中的两种实践
- 优先选择:传递中断
下游服务的方法直接抛出异常,让上游服务去进行处理。要注意的是run()不能继续向上抛异常,因为是重写的run接口,该接口本身不抛异常。
/**
* 描述: 最佳实践:catch了InterruptedExcetion之后的优先选择:在方法签名中抛出异常 那么在run()就会强制try/catch
*/
public class RightWayStopThreadInProd implements Runnable {
@Override
public void run() {
while (true && !Thread.currentThread().isInterrupted()) {
System.out.println("go");
try {
throwInMethod();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
//保存日志、停止程序
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:在catch子语句中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断
* 回到刚才RightWayStopThreadInProd补上中断,让它跳出
*/
public class RightWayStopThreadInProd2 implements Runnable {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted,程序运行结束");
break;
}
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();
}
}
- 不应该屏蔽中断
3、错误的停止方法
- 被弃用的stop,suspend,resume方法
但是stop方法确实会关掉monitor的锁。什么是monitor

- 用volatile设置boolean标记位
4、停止线程相关方法
-
static boolean interrupted()判断是否中断,该方法会清除中断标志位 -
boolean isinterrupted()判断是否中断,该方法不会清除中断标志位 -
Thread.interrupted()的目的对象是当前线程,而不管使用的方法来自于哪个对象。
// 启动线程
threadOne.start();
//设置中断标志
threadOne.interrupt();
//获取中断标志
System.out.println("isInterrupted: " + threadOne.isInterrupted());
//获取中断标志并重置
System.out.println("isInterrupted: " + threadOne.interrupted());
//获取中断标志并重直
System.out.println("isInterrupted: " + Thread.interrupted());
//获取中断标志
System.out.println("isInterrupted: " + threadOne.isInterrupted());
答案是 true,false(调用静态方法,实际上是判断main的线程是否中断),false(同上),true(第一个isInterupted()是实例方法,不会清除中断标志位)
问题:如何停止线程
-
原理:用interrupt请求,好处(保证数据安全,把主动权交给被中断方)
-
想要停止线程,需要请求方(发出中断请求的一方),被停止方(响应中断的一方,需要配合请求方发出的中断,比如检查中断对其响应),子方法(被停止方的线程中可能调用到的方法,如果这里面有中断等情况,最好向上抛出,如果不行则传递)
-
stop/suspend已废弃,volatile的boolean无法处理长时间阻塞的情况
sychronize(u)
{
//这里长时间阻塞,那么u的状态改变没办法改变其阻塞状态
}