Java一个线程能否结束另一个永不停止的线程

在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()方法是可以派上用场的。

参考:Java中如何实现线程的超时中断

 

修改子线程的代码:

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中正确停止Thread?

你如何杀死Java中的线程?

如何在运行时停止/终止长时间运行的Java线程?超时 - >取消 - >中断状态

Java - 从不同的类停止线程

https://javaconceptoftheday.com/how-to-stop-a-thread-in-java/

如何超时一个线程

 

参考《Effective Java 中文版 第3版

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值