Java 中如何终止一个正在运行的线程

目录

1、使用 volatile 标志位

2、stop() 强制终止(已废弃)

3、interrupt() 协作式中断


线程的调度和执行由操作系统决定,而 Java 通过提供一系列 API 来与操作系统交互,从而实现对线程的控制。

现代操作系统对线程的控制遵循两个核心原则:

  1. 抢占式调度:操作系统可以强制剥夺线程的 CPU 时间片(如时间片轮转调度)

  2. 协作式中断:依赖线程主动响应中断信号(Java 的 interrupt()

所以,我们可以知道,Java 虚拟机(JVM)作为用户态程序,其实是无法直接终止一个线程的。当我们调用 thread.stop() 这类方法时,实际上是在用户态层面发出中断请求,而真正的线程终止还需要操作系统内核的配合。

Java 线程中断的三种实现方式

  • volatile 标志位:通过设置标志位,使线程正常退出,能够确保线程根据业务逻辑安全停止。

  • Thread.stop() 方法:已废弃,不推荐使用,因为会导致线程安全问题和资源泄漏。

  • Thread.interrupt() 方法:推荐使用,通过设置中断标志来提示线程停止,能够优雅的停止线程。

1、使用 volatile 标志位

volatile 关键字可以确保变量的修改对所有线程立即可见。通过在共享变量上使用 volatile,工作线程可以在运行过程中检查该变量的状态,一旦检测到变化即可清理资源并结束执行。

public class WorkerThread extends Thread {
    privatevolatileboolean running = true;

    public void run() {
        int i = 1;
        while (running) { // 检查标志位
            System.out.println("线程运行中: " + i++);
            try {
                Thread.sleep(1000L); // 模拟业务操作
            } catch (InterruptedException e) {
                // 重新设置中断标识
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("线程已优雅停止");
    }

    public void cancel() {
        running = false;
    }

    public static void main(String[] args) throws InterruptedException {
        WorkerThread thread = new WorkerThread();
        thread.start();
        // 线程运行一段时间
        Thread.sleep(5000L);

        // 修改标志位,中断线程
        thread.cancel();
    }
}

运行结果:

线程运行中: 1
线程运行中: 2
线程运行中: 3
线程运行中: 4
线程运行中: 5
线程已优雅停止

2、stop() 强制终止(已废弃)

Thread.stop()方法是 Java 早期提供的一种强制中止线程的方式,该方法会立即终止线程,而不执行清理工作(如释放资源、完成文件操作等),因此容易导致资源泄漏和线程安全问题,已被 Java 官方弃用。

public class WorkerThread extends Thread {
    public void run() {
        int i = 1;
        while (true) {
            System.out.println("线程运行中: " + i++);
            try {
                Thread.sleep(1000L); // 模拟业务操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        WorkerThread thread = new WorkerThread();
        thread.start();
        // 线程运行一段时间
        Thread.sleep(5000L);

        // 调用 stop 方法,强制终止线程
        thread.stop();
        System.out.println("线程已强制终止");
    }
}

运行结果:

线程运行中: 1
线程运行中: 2
线程运行中: 3
线程运行中: 4
线程运行中: 5
线程已强制终止

为什么 stop() 方法会被废弃呢? JavaDoc 中Why is Thread.stop deprecated?这篇官方文档解释了其被废弃的原因。总结如下:

  • 数据不一致:当一个线程被强制停止时,它所持有的所有锁都会被立即释放。这种情况可能会使其他线程读取到未完成或不一致的状态,进而基于错误的数据做出决策,造成系统内部的数据混乱和逻辑错误。

  • 资源无法释放:使用Thread.stop()强制终止线程,会突然中断线程正在进行的操作,抛出 ThreadDeath 异常,不会给线程去正常关闭或清理资源(比如 finally 块中的语句)的机会。所以,这可能会导致一些资源释放工作无法完成,从而引发资源泄露或者其它问题。

3、interrupt() 协作式中断

因为 Thread.stop() 中断线程的粗暴性,Java 官方已经废弃了该方法,那么官方还有没有其它更优雅的方案来中断线程呢?答案是有的:那就是使用 interrupt() 协作式中断。

什么是协作式中断?

协作式中断就是区别于 Thread.stop(),它是非强制且需要配合的。什么意思呢?

  • 非强制性interrupt() 方法不会强制终止线程,而是通过设置中断标志来通知线程应该停止。线程可以选择中断,也可以忽略这个中断请求,继续执行。

  • 需要配合:使用 interrupt() 方法,线程需要主动检查中断标志,或者在阻塞方法中处理 InterruptedException,才能正常响应中断请求。这种机制是需要线程和中断请求者之间相互“配合”的。

协作式中断就像在办公室中,你的小伙伴正在对着电脑努力“搬砖”。到了干饭时间,他却还没有停下来。这时你不能直接搬走他的电脑或强制他离开桌子,而是趴在他耳边轻声地说:“嘿,兄弟,干饭啦。” 他听到后可以选择停下来,整理自己当前的任务,然后离开,也可以继续牛马。这就是协作式的,因为你只是提醒他需要停止,最终是否停止取决于他的回应。

下面,我们通过示例来演示下协作式中断:

反例

public class WorkerThread extends Thread {

    @Override
    public void run() {
        int i = 1;
        while (!Thread.currentThread().isInterrupted()) { // 显式检查中断状态
            System.out.println("线程运行中: " + i++);
            try {
                Thread.sleep(1000L); // 模拟业务操作
            } catch (InterruptedException e) {
                System.out.println("捕获到中断异常,但线程继续运行!");
//                Thread.currentThread().interrupt();
            }
        }
        System.out.println("线程已优雅停止");
    }

    public static void main(String[] args) throws InterruptedException {
        WorkerThread thread = new WorkerThread();
        thread.start();
        Thread.sleep(5000L); // 主线程等待5秒

        // 请求线程中断
        thread.interrupt();
        System.out.println("主线程请求线程中断");
    }
}

 运行结果:

线程运行中: 1
线程运行中: 2
线程运行中: 3
线程运行中: 4
线程运行中: 5
主线程请求线程中断
捕获到中断异常,但线程继续运行!
线程运行中: 6
线程运行中: 7
......

可以看到,main() 方法中已经请求了线程中断,但是线程并没有被中断,还是继续执行下去了,为什么?

interrupt() 方法被调用时,线程的中断标志会被设置为 true。它的调用只是传递了一个中断信号,线程本身不会立即停止执行。它需要通过检查中断标志(如使用 Thread.currentThread().isInterrupted())来决定是否停止当线程。

并且当线程处于以下阻塞方法(如 sleep()wait()join())时,JVM 会做两件事:1.抛出 InterruptedException 异常;2.清除中断标志,重新设置为 false

那么,当我们在程序中捕获到 InterruptedException 异常时,我们需要做出明确的选择:

  • 处理异常并终止线程的执行

  • 重新设置中断标识让上层逻辑感知到

上边的示例中,我们在捕获了 InterruptedException 异常后,这时中断标志已经被重置,而程序中却没有做任何的处理,导致了在下一轮的循环判断中,检测的中断状态不对,所以线程没有按预期执行中断。

正例

public class WorkerThread extends Thread {

    @Override
    public void run() {
        int i = 1;
        while (!Thread.currentThread().isInterrupted()) { // 显式检查中断状态
            System.out.println("线程运行中: " + i++);
            try {
                Thread.sleep(1000L); // 模拟业务操作
            } catch (InterruptedException e) {
                // 重新设置中断标志,通知上层逻辑进行中断处理
                System.out.println("线程被中断!");
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("线程已优雅停止");
    }

    public static void main(String[] args) throws InterruptedException {
        WorkerThread thread = new WorkerThread();
        thread.start();
        Thread.sleep(5000L); // 主线程等待5秒

        // 请求线程中断
        thread.interrupt();
        System.out.println("主线程请求线程中断");
    }
}

运行结果:

线程运行中: 1
线程运行中: 2
线程运行中: 3
线程运行中: 4
线程运行中: 5
主线程请求线程中断
线程被中断!
线程已优雅停止

在这个示例中,我们在捕获到异常后,正确的恢复了中断标志,线程优雅地被中断。

总结:在实际开发中,推荐使用 volatile 标志位和 thread.interrupt() 协作式中断来中止线程。这两种方式能够确保线程的安全和资源的正确释放,避免线程不安全和资源泄漏问题。thread.stop() 方法由于其强制性和不安全性,已被废弃,不推荐使用。通过合理使用协作式中断机制,可以实现线程的优雅停止,提高程序的健壮性和可靠性。

本文引自:阿里一面:Java 中如何终止一个正在运行的线程

### Java 终止正在运行线程Java,直接强制停止一个线程并不是最佳实践。`Thread.stop()`方法虽然能够立即终止线程,但由于其不安全性以及可能引发的资源泄漏问题,在现代编程实践应避免使用此方法[^2]。 更推荐的做法是通过协作方式让线程自行退出循环,即设置标志位告知线程应该结束工作并正常返回。对于长时间阻塞的任务(如等待I/O完成),应当调用`interrupt()`断该线程,并在其内部捕获`InterruptedException`异常以响应断请求[^1]。 下面是一个利用`volatile`变量作为控制信号的例子: ```java public class SafeStopThread extends Thread { // 使用 volatile 关键字确保可见性 private volatile boolean running = true; @Override public void run() { System.out.println("线程开始..."); while (running) { try { // 假设这里有一些耗时操作 Thread.sleep(100); } catch (InterruptedException e) { // 当前线程断,则跳出循环准备退出 break; } } System.out.println("线程结束"); } /** * 安全地通知线程停止 */ public void requestStop() { this.running = false; interrupt(); // 如果有需要的话还可以显式触发断 } } ``` 上述代码展示了如何创建一个可安全关闭的线程实例。当希望终止线程时只需调用`requestStop()`即可优雅地线程的工作而不造成任何副作用[^3]。 此外,为了实现更加复杂的场景下的线程管理,比如既允许暂停又允许恢复的情况,可以通过引入额外的状态标记来达成目的。例如结合`wait()/notifyAll()`机制可以让线程进入等待状态直到收到唤醒指令再继续执行[^4]。 #### 注意事项 - 对于那些处于无限循环线程来说,仅仅依靠`interrupt()`并不足以使其真正停下来;还需要在线程体内定期检查是否发生了断事件。 - `isInterrupted()`用于判断当前是否有未处理过的断请求存在,而`Thread.interrupted()`则会清除这一状态标志位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值