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() 的三大罪
- 不释放锁:线程可能在持有锁时被终止,导致其他线程永远等不到锁
- 数据不一致:操作执行一半就被打断,数据停留在中间状态
- 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()挂起线程 - 在不了解业务的情况下直接杀线程
