Skip to content

stop/suspend/resume:为什么被废弃

这些方法不是不能用,而是用了会出问题。

先看一个问题

你接手了一个老项目,看到这样的代码:

java
Thread worker = new Thread(() -> {
    while (true) {
        processTask();
    }
});
worker.start();

// 某处代码
worker.stop();  // 停止它

STOP?Java 不是不推荐用 stop 吗?为什么?

三个废弃方法一览

方法废弃版本废弃原因
stop()JDK 1.2强制终止,不释放锁,数据不一致
suspend()JDK 1.2挂起不释放锁,容易死锁
resume()JDK 1.2和 suspend 配套废弃

这三个方法在 JDK 1.2 就被废弃了,到现在都快 30 年了。

stop() 的问题

stop() 做了什么

stop()强制终止线程,不管它执行到哪一行代码。

java
public class StopDemo {

    private static Object lock = new Object();
    private static int balance = 1000;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                // 模拟转账过程
                System.out.println("开始转账...");
                balance -= 500;  // 第一步:减
                // 假设这里被 stop() 了
                balance += 500;  // 第二步:加回来(没机会执行)
                System.out.println("转账完成");
            }
        });

        t1.start();
        Thread.sleep(100);
        t1.stop();  // 强制终止!

        System.out.println("余额: " + balance);  // 可能是 500,丢了 500 块!
    }
}

问题分析

正常流程:
balance = 1000 → 1000-500=500 → 500+500=1000 ✅

被 stop() 后:
balance = 1000 → 1000-500=500 → [被 stop()] → 钱丢了!❌

stop() 的三大罪

  1. 不释放锁:线程可能在持有锁时被终止,导致其他线程永远等不到锁
  2. 数据不一致:操作执行一半就被打断,数据停留在中间状态
  3. finally 不执行try {} finally {} 里的清理代码可能根本没机会执行
java
// stop() 可能导致 finally 不执行
Thread t = new Thread(() -> {
    try {
        FileOutputStream fos = new FileOutputStream("data.txt");
        fos.write(data);
        fos.flush();
        // 假设这里被 stop()
        fos.close();  // 没机会执行,文件句柄泄漏
    } finally {
        // 不保证执行
    }
});

suspend()/resume() 的问题

suspend() 做了什么

suspend()挂起线程,让它暂停,但不释放锁。

java
public class SuspendDemo {

    private static Object resource = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (resource) {
                System.out.println("线程A: 拿到锁,suspend 自己");
                Thread.currentThread().suspend();  // 挂起,但没释放锁!
                System.out.println("线程A: 恢复");
            }
        });

        t1.start();
        Thread.sleep(100);

        Thread t2 = new Thread(() -> {
            synchronized (resource) {  // 等锁,永远等不到
                System.out.println("线程B: 拿到锁了");
            }
        });

        t2.start();

        // t2 会永远卡住,因为 t1 持有锁但不干活
    }
}

死锁原因

线程A:synchronized(resource) → 拿到锁 → suspend() → 卡住(但锁还在手里)
线程B:synchronized(resource) → 等锁 → 永远等不到

resume() 有什么用

resume() 用来恢复被 suspend() 挂起的线程。但问题来了:

java
Thread t = new Thread(() -> {
    synchronized (lock) {
        Thread.currentThread().suspend();  // 挂起,持有锁
    }
});

t.start();
t.resume();  // 恢复,但如果锁还被其他线程持有呢?

如果 resume() 调用时,锁还被其他线程持有,那么被挂起的线程即使被恢复,也拿不到锁,还得继续等。

正确停止线程的方式

方式一:标志位 + interrupt()

java
public class GracefulStop implements Runnable {

    private volatile boolean running = true;

    @Override
    public void run() {
        while (running && !Thread.currentThread().isInterrupted()) {
            try {
                doWork();
            } catch (InterruptedException e) {
                // 被中断了,恢复状态,优雅退出
                Thread.currentThread().interrupt();
                break;
            }
        }
        cleanup();
    }

    public void stop() {
        running = false;
    }

    public void shutdown() {
        running = false;
        Thread.currentThread().interrupt();  // 额外中断
    }
}

方式二:线程池终止

java
ExecutorService executor = Executors.newFixedThreadPool(4);

// 提交任务
Future<?> future = executor.submit(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // 干活
    }
});

// 方式1:优雅关闭(不接收新任务,等待现有任务完成)
executor.shutdown();
boolean success = executor.awaitTermination(1, TimeUnit.MINUTES);

// 方式2:立即停止(尝试停止所有任务)
if (!success) {
    executor.shutdownNow();
}

// 方式3:带超时的优雅关闭
executor.shutdown();
if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
    executor.shutdownNow();
}

方式三:CompletableFuture 取消

java
CompletableFuture.supplyAsync(() -> doWork())
    .orTimeout(10, TimeUnit.SECONDS)
    .exceptionally(ex -> {
        // 处理超时
        return null;
    });

中断的正确处理

捕获 InterruptedException

java
// ❌ 错误:吞掉中断
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // 什么都没做,中断信息丢失
}

// ✅ 正确:恢复中断状态
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 恢复中断标志
}

// ✅ 也正确:抛出异常
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}

在循环中正确响应中断

java
// ❌ 错误:while 里没检查中断
while (running) {
    try {
        process();
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        // 继续循环,假装什么都没发生
    }
}

// ✅ 正确:捕获后主动退出
while (running && !Thread.currentThread().isInterrupted()) {
    try {
        process();
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        // 被中断了,优雅退出
        break;
    }
}

总结对比

废弃方法问题替代方案
stop()强制终止,不释放锁,finally 不执行标志位 + interrupt()
suspend()挂起不释放锁,容易死锁wait() / Condition.await()
resume()配套 suspend 废弃notify() / Condition.signal()

正确停止线程的流程

1. 设置停止标志(running = false)
2. 调用 interrupt()(让阻塞中的线程抛出异常)
3. 等待线程自然结束(join())
4. 在 finally 中清理资源

永远不要

  • stop() 强制终止
  • suspend() 挂起线程
  • 在不了解业务的情况下直接杀线程

基于 VitePress 构建