本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
【腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
这个月的 3 月 17 号,JDK 26 正式发布了,昨天我也写了对应的文章,给大家广而告之。
昨天和之前的一系列文章中,我更多讲的是 JEP 这类的新特性等内容。这次的 JDK 26 发布,还有一些非特性方面的内容很少有网友关注。后面,我就抽出一些时间来一一讲解一下 JDK 26 中容易被忽略遗忘的改进!
本文重点讲解要给,Java 老司机都熟悉的线程方法Thread.stop(),这个方法被 Java 26 正式“枪毙”了,一个被诅咒了 28 年的方法终于入土了。
早在去年 5 月份,我就写过一篇文章《https://mp.weixin.qq.com/s/twymMnZmShtQ-1iOeb-m3Q》,讲过 Java 线程的两阶段停止以及不推荐的停止方式。
- Thread.stop():已废弃,强制停止线程可能导致对象状态不一致。类似 kill -9 一样的危害
- Thread.suspend()、resume():已废弃,容易导致死锁
现在 Java 26 正式移除了 Thread.stop(),一个遗留 28 年的“定时炸弹”终于谢幕。它也是 Java 史上最危险的 API,还曾让无数系统数据崩溃。
文章配图参见我的公众号https://mp.weixin.qq.com/s/zucWddfzu-bPtcHCqnQW3w。
JDK-8368226 到底做了什么?
JDK-8368226 虽不起眼,但由于它的历史悠久,因此也算得上是一个重磅变更了。尤其是在这个看似常规的短期支持版本中,一个看似微小但意义深远的变更java.lang.Thread.stop() 方法被彻底移除了。
文章配图参见我的公众号https://mp.weixin.qq.com/s/zucWddfzu-bPtcHCqnQW3w。
根据 OpenJDK 官方发布说明 ,这次变更(编号#JDK-8368226)意味着。
- 编译层面:调用
Thread.stop()的代码将无法通过编译 - 运行层面:已编译的旧代码在 Java 26 上运行时会抛出
NoSuchMethodError(而非之前的UnsupportedOperationException)
这不是一个简单的“废弃”(deprecated),而是“物理删除”。从 JDK 的源代码、字节码到运行时,这个方法彻底消失了。
黑历史回顾
要理解这次移除的意义,我们需要回顾 Thread.stop() 的“黑历史”。
| 时间节点 | 事件 | 意义 |
|---|---|---|
| 1998 年 | JDK 1.2 发布,Thread.stop() 被标记为 @Deprecated | 官方首次承认其不安全 |
| 2004 年 | Java 官方文档《Why Are Thread.stop, Thread.suspend and Thread.resume Deprecated?》发布 | 详细解释了不安全原因 |
| 2022 年 | JDK 18 发布,标记为 @Deprecated(forRemoval = true) | 正式进入移除倒计时 |
| 2023 年 | JDK 20 发布,改为无条件抛出 UnsupportedOperationException | 运行时层面禁用 |
| 2026 年 | JDK 26 发布,方法彻底移除 | 28 年的“废弃生涯”终结 |
这个方法从被标记废弃到真正移除,经历了整整 28 年,多深的包袱呀!这在 Java 历史上是罕见的超长“ deprecation 周期”。
28 年!从一个方法从废弃到删除,就可以看出 Java 的“向后兼容”到底是有多么的执着。
为什么 Thread.stop() 如此危险?
官方文档用了一句话概括:This method is inherently unsafe(这个方法本质上就是不安全的) 。
两大致命缺陷
当你调用 thread.stop() 时,背后会发生两件事。
- 即刻抛出
ThreadDeath异常:这个异常可以在run()方法的任何执行点抛出,包括catch和finally块中 强制释放所有锁:线程持有的所有监视器(monitor)会被立即解锁
数据损坏的典型案例
想象下面这样一个场景。
public class BankTransfer {
private double balance = 1000;
public synchronized void transfer(double amount) {
// 步骤1:扣减余额
this.balance -= amount; // 此时 balance = 500
// 步骤2:模拟耗时操作(网络请求、日志记录等)
Thread.sleep(5000); // 在此期间被 stop()!
// 步骤3:记录交易流水(永远执行不到了)
logTransaction(amount);
}
}
如果在 sleep 期间调用 stop(),会发生什么?
- 交易未完成,但余额已经被扣减(
balance = 500) - 锁被释放,其他线程可以立即访问这个
不一致状态的对象 - 没有交易记录,但钱已经“消失”了
这就是官方所说的damaged objects(受损对象),处于不一致状态的对象被暴露给其他线程,可能导致任意不可预测的行为。
为什么不能捕获 ThreadDeath 来修复?
理论上,你可以在catch (ThreadDeath)中做清理工作。但官方文档明确指出这几乎不可能。
- 异常可以在任何地方抛出:你需要在每个同步块、每个方法调用处都考虑清理逻辑,代码将变得极其复杂
- 清理过程中可能再次抛出
ThreadDeath:套娃了,你需要递归地处理清理,直到成功,这在实践中几乎无法实现
更可怕的是,ThreadDeath是一个Error,而非 Exception,它悄无声息地杀死线程,不会打印堆栈跟踪(除非你显式捕获),这也导致产生问题后极难排查。
Java 社区的移除共识
这次移除并非突然决定,而是 Java 社区多年共识的结果。
Project Loom 的推动
随着 Project Loom(虚拟线程)的推进,Java 的并发模型正在发生根本性变革。Thread.stop()这种“野蛮”的线程终止方式,与现代化、结构化的并发编程理念完全冲突。在结构化并发(Structured Concurrency)中,线程的生命周期应该是协作式、可预测、可组合的,而非被强制终止。
Java 安全性的整体提升
JDK 26 中还有另一个相关变更《JEP 500 – Prepare to Make Final Mean Final》,https://mp.weixin.qq.com/s/L0vWK5BTQrlO_huLbOtC9Q。这个特性警告使用深度反射修改final字段的行为,为未来彻底禁止这种操作做准备。
Thread.stop()的移除与这一趋势一致,即:Java 正在收紧那些破坏对象完整性、绕过语言安全机制的操作。
正确停止线程的现代方案
既然 stop() 被移除了,我们应该如何正确停止线程?
方案 1 协作式中断
看下面这段代码。
public class SafeStop {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
// 定期检查中断状态
while (!Thread.currentThread().isInterrupted()) {
try {// 业余草
doWork();
} catch (InterruptedException e) {
// 收到中断信号,清理资源后退出
System.out.println("收到中断信号,准备退出...");
cleanup();
break; // 或 return
}
}
});
worker.start();
Thread.sleep(1000);
worker.interrupt(); // 优雅地请求停止
}
}
方案 2 volatile 标志位(推荐)
public class VolatileStop {
private volatile boolean running = true;
// 业余草
public void stop() {
running = false;
}
public void run() {
while (running) {
doWork();
}
cleanup();
}
}
方案 3 结构化并发
这个结构化并发方案需要在 JDK 21+ 的版本上才能体验到。
// 使用 StructuredTaskScope 自动管理子线程生命周期
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
scope.fork(() -> {
// 业余草 子任务1
return doTask1();
});
scope.fork(() -> {
// 业余草 子任务2
return doTask2();
});
scope.join(); // 等待所有任务完成或失败
scope.throwIfFailed();
} // 作用域结束时,所有子线程自动、安全地停止
对现有项目的影响与迁移建议
影响范围评估
- 直接调用
Thread.stop():编译失败,必须修改 - 反射调用:会抛出
NoSuchMethodError - 遗留系统:一些老旧的库(如早期版本的某些应用服务器)可能还在使用
迁移步骤
- 全局搜索:在代码库中搜索
.stop()调用 - 替换为中断机制:将强制停止改为
interrupt()+ 协作式检查 - 测试边界情况:确保中断响应逻辑正确处理资源清理
- 升级依赖:确保第三方库已适配 Java 26
临时回退方案
如果必须运行依赖 stop() 的旧代码,则可以考虑下面两种做法。
- 在 Java 25(LTS 版本)上运行,获得长期支持
- 使用字节码转换工具(如 ASM)将
stop()调用重定向到自定义的安全停止逻辑
Java 的技术债清理
Thread.stop()的移除,标志着 Java 平台正在加速清理历史遗留的技术债。
近年来,我们已经看到。
- JDK 17:移除 Applet API、Security Manager
- JDK 21:弃用 32 位 Windows 支持
- JDK 26:移除
Thread.stop()、Applet API(彻底移除)、InfiniBand SDP 支持
这些变更的共同点是,它们都是在 1990 年代或 2000 年代初设计的 API,在当时有其合理性,但与现代计算环境完全不兼容。
Java 不再是那个“永远向后兼容”的保守平台。在保持企业级稳定性的同时,它正在有节奏地剔除那些阻碍现代化、带来安全隐患的古老特性。
结语
Java 正在向“不安全”告别,也在向“永远向后兼容”告别。
Thread.stop() 的移除,是 Java 并发编程历史上的一个里程碑。它宣告了一个时代的结束:强制、不可控、破坏性的线程管理方式,终于被协作式、结构化、安全的方式取代。
对于开发者来说,这既是挑战(需要检查遗留代码),也是机遇(被迫采用更健壮的并发模式)。正如官方文档所言:
能协作的线程才是好线程,需要被 stop 的线程本就不该 start。
Stop 一个线程,等于承认你的设计失败了两次:一次是让它做不该做的事,一次是你不得不杀了它。
如果你需要 Thread.stop(),那你需要的不是 stop,是重构。
28 年的等待,Thread.stop() 终于谢幕。这是 Java 平台向现代化、安全性、结构化并发迈出的坚实一步。
参考资料
- OpenJDK JDK 26 Release Notes
https://jdk.java.net/26/release-notes - Java 官方文档《Why Are Thread.stop, Thread.suspend and Thread.resume Deprecated?》
https://stackoverflow.com/questions/8464368/how-can-i-stop-threads-created-with-an-anonymous-classhttps://bugs.openjdk.org/browse/JDK-8368226

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » JDK 26 释放重磅信号, 不再包容屎山代码,移除 Thread.stop