在Java中停止一个线程有三种办法 :
1.正常结束执行;
2.发生异常;
3.被其他线程stop(Java官方不建议)
参考:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
为什么Thread.stop
弃用?
因为它本质上是不安全的。停止线程会导致它解锁已锁定的所有监视器。(当ThreadDeath
异常传播到堆栈中时,监视器将被解锁。)如果先前受这些监视器保护的任何对象处于不一致状态,则其他线程现在可以以不一致的状态查看这些对象。据说这些物体已被 损坏。当线程对受损对象进行操作时,可能会导致任意行为。这种行为可能很微妙并且难以检测,或者可能是明显的。与其他未经检查的异常不同,它会 ThreadDeath
默默地杀死线程; 因此,用户没有警告他的程序可能被破坏。
所以如果遇到一种特殊情况某一个线程A会一直执行下去停不下来,这种情况是存在的比如那种需要持续取样的线程A,当然了在正常代码里会有“停止”功能,外部线程B可以发送停止信号给A,A可以直接结束。
如果A线程没有这种信号量那么B线程还可以主动停止他么?答案是不可以!
public class Test {
public static void main(String args[]) throws InterruptedException {
Thread thread1 = new Thread() {
public void run() {
fun_a();
}
};
thread1.start();
int a = 0;
while (a < 100) {
Thread.sleep(1000);
a++;
if (a == 3) {
a = 100;
thread1.interrupt();
//thread1.stop();
//throw new RuntimeException("主函数抛出异常");
}
}
}
public static void fun_a() {
for (; ; ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(">> " + DateUtil.getNowTimeString());
}
}
}
可以看到interrupt并不能让运行中的线程停止,这个是很容易被误解的地方。这个方法的作用并不是中断线程,而是设置一个标识,通知该线程可以被中断了,到底是继续执行,还是中断返回,由线程本身自己决定。
当对一个线程调用了interrupt()
之后,如果该线程处于被阻塞状态(比如执行了wait、sleep或join等方法),那么会立即退出阻塞状态,并抛出一个InterruptedException
异常,在代码中catch这个异常进行后续处理。如果线程一直处于运行状态,那么只会把该线程的中断标志设置为 true,仅此而已,所以interrupt()
并不能真正的中断线程,不过在rpc调用的场景中,请求线程一般都处于阻塞状态,等待数据返回,这时interrupt()
方法是可以派上用场的。
修改子线程的代码:
public static void fun_a() {
for (; ; ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException("子线程抛出异常");
//e.printStackTrace();
}
System.out.println(">> " + DateUtil.getNowTimeString());
}
}
这次是可以结束子线程,前提是子线程自己有异常捕获机制,可以接受其他线程发来的InterruptedException:
主线程每隔2秒对子线程进行一次Interrupted:
package com.t.www;
public class Test {
final static Object lock = new Object();
volatile boolean stop = false;
public static void main(String args[]) throws InterruptedException {
Thread thread1 = new Thread() {
public void run() {
fun_a();
}
};
thread1.start();
System.out.println("> 1 主线程start " + DateUtil.getNowTimeString());
int a = 0;
while (a < 3) {
Thread.sleep(2000);
a++;
System.out.println("> 2 主线程对子线程开始interrupt " + DateUtil.getNowTimeString());
thread1.interrupt();
System.out.println("> 3 主线程对子线程完成interrupt " + DateUtil.getNowTimeString());
}
}
public static void fun_a() {
for (; ; ) {
try {
System.out.println(">> 1 子线程wait " + DateUtil.getNowTimeString());
synchronized (lock) {
lock.wait();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
//throw new RuntimeException("子线程抛出异常");
e.printStackTrace();
}
System.out.println(">> 2 子线程完成 " + DateUtil.getNowTimeString());
}
}
}
修改子线程代码:
try {
System.out.println(">> 1 子线程wait " + DateUtil.getNowTimeString());
synchronized (lock) {
lock.wait();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
//throw new RuntimeException("子线程抛出异常");
//e.printStackTrace();
System.out.println(">> 2 子线程捕获异常 " + DateUtil.getNowTimeString());
}
从运行结果看和前面一致,只是没有抛出异常。
修改代码子线程使用while(!Thread.currentThread().isInterrupted())判断:
public class Test {
public static void main(String args[]) throws InterruptedException {
Thread thread1 = new Thread() {
public void run() {
fun_a();
}
};
thread1.start();
System.out.println("> 1 主线程start " + DateUtil.getNowTimeString());
int a = 0;
while (a < 3) {
Thread.sleep(2000);
a++;
System.out.println("> 2 主线程对子线程开始interrupt " + DateUtil.getNowTimeString());
thread1.interrupt();
System.out.println("> 3 主线程对子线程完成interrupt " + DateUtil.getNowTimeString());
}
}
public static void fun_a() {
while(!Thread.currentThread().isInterrupted()){
try {
System.out.println(">> 1 子线程 " + DateUtil.getNowTimeString());
Thread.sleep(1000);
} catch (InterruptedException e) {
//throw new RuntimeException("子线程抛出异常");
//e.printStackTrace();
System.out.println(">> 2 子线程捕获异常 " + DateUtil.getNowTimeString());
}
System.out.println(">> 3 子线程完成 " + DateUtil.getNowTimeString());
}
System.out.println(">> 4 子线程正常结束 " + DateUtil.getNowTimeString());
}
}
可以看到这次因为子线程增加了状态判断所以可以正常结束:
比较优雅的方式是使用一个变量在线程间通信,需要注意的是要保证可见性:
public class Test {
private static volatile boolean finished = false; // ① volatile条件变量
public static void main(String args[]) throws InterruptedException {
Thread thread1 = new Thread() {
public void run() {
fun_a();
}
};
thread1.start();
System.out.println("> 1 主线程start " + DateUtil.getNowTimeString());
int a = 0;
while (a < 3) {
Thread.sleep(2000);
a++;
}
System.out.println("> 1 主线程 a=" +a+" "+ DateUtil.getNowTimeString());
finished=true;
}
public static void fun_a() {
while(!finished){
try {
System.out.println(">> 1 子线程 " + DateUtil.getNowTimeString());
Thread.sleep(1000);
} catch (InterruptedException e) {
//throw new RuntimeException("子线程抛出异常");
//e.printStackTrace();
System.out.println(">> 2 子线程捕获异常 " + DateUtil.getNowTimeString());
}
System.out.println(">> 3 子线程完成 " + DateUtil.getNowTimeString());
}
System.out.println(">> 4 子线程正常结束 " + DateUtil.getNowTimeString());
}
}
------------------
如何停止线程或任务
如何停止一个正在运行的java线程
了解Java中的线程中断
如何在运行时停止/终止长时间运行的Java线程?超时 - >取消 - >中断状态
https://javaconceptoftheday.com/how-to-stop-a-thread-in-java/
参考《Effective Java 中文版 第3版》