如何终止一个 Java 线程

更新于 2025-12-29

Eric Goebelbecker 2024-09-07

1. 引言

在本简短文章中,我们将介绍如何在 Java 中停止一个线程——这并不简单,因为 Thread.stop() 方法已被弃用。

正如 Oracle 的这篇更新说明 所解释的那样,stop() 方法可能导致被监控对象的状态被破坏(即对象处于不一致或损坏状态)。

2. 使用标志位(Flag)

我们首先创建一个类,用于启动并管理一个线程。该任务本身不会自动结束,因此我们需要一种方式来安全地停止该线程。

为此,我们将使用一个原子布尔标志(AtomicBoolean):

public class ControlSubThread implements Runnable {

    private Thread worker;
    private final AtomicBoolean running = new AtomicBoolean(false);
    private int interval;

    public ControlSubThread(int sleepInterval) {
        interval = sleepInterval;
    }
 
    public void start() {
        worker = new Thread(this);
        worker.start();
    }
 
    public void stop() {
        running.set(false);
    }

    public void run() { 
        running.set(true);
        while (running.get()) {
            try { 
                Thread.sleep(interval); 
            } catch (InterruptedException e){ 
                Thread.currentThread().interrupt();
                System.out.println(
                  "线程被中断,未能完成操作");
            }
            // 在此处执行某些任务
         } 
    } 
}

这里我们没有使用一个恒为 truewhile 循环,而是使用了 AtomicBoolean,这样就可以通过将其设为 truefalse 来控制线程的启动与停止。

正如我们在《原子变量简介》一文中所解释的,使用 AtomicBoolean 可以避免多个线程在设置和读取该变量时发生冲突。

3. 中断线程(Interrupting a Thread)

如果 sleep() 的时间间隔设置得很长,或者线程正在等待一个可能永远不会释放的锁,会发生什么情况?

这时就存在长时间阻塞甚至无法干净终止的风险。

为应对这些情况,我们可以使用 interrupt() 方法。让我们在类中添加几个方法和一个新的标志:

public class ControlSubThread implements Runnable {

    private Thread worker;
    private AtomicBoolean running = new AtomicBoolean(false);
    private AtomicBoolean stopped = new AtomicBoolean(false);
    private int interval;

    // ...

    public void interrupt() {
        running.set(false);
        worker.interrupt();
    }

    boolean isRunning() {
        return running.get();
    }

    boolean isStopped() {
        return stopped.get();
    }

    public void run() {
        running.set(true);
        stopped.set(false);
        while (running.get()) {
            try {
                Thread.sleep(interval);
            } catch (InterruptedException e){
                Thread.currentThread().interrupt();
                System.out.println(
                  "线程被中断,未能完成操作");
            }
            // 执行某些任务
        }
        stopped.set(true);
    }
}

我们新增了一个 interrupt() 方法,它会将 running 标志设为 false,并调用工作线程的 interrupt() 方法。

如果在线程处于 sleep() 状态时调用此方法,sleep() 将抛出 InterruptedException(其他阻塞调用也是如此)。

这会使线程跳出阻塞状态回到循环中,并由于 running 已为 false 而正常退出。

4. 结论

在本快速教程中,我们探讨了如何使用原子变量(AtomicBoolean),并可选择性地结合调用 interrupt() 方法,来干净地关闭一个线程。这种方式显然优于调用已弃用的 stop() 方法,后者可能导致永久死锁和内存损坏等问题。