本地单测通过,线上跑几分钟后工作线程再也不退出,jstack 显示它停在 while 循环里,stopped 已经被另一个线程改成 true。乍看像缓存或者权限的锅,追下去往往不是。

private volatile boolean stopped = false;
// 复现参数
java -XX:-TieredCompilation -XX:CompileThreshold=10000 -jar app.jar
jstack 21455 | grep -A 5 'while'

追下去是这么回事:stopped 不是 volatile,C2 在方法编译后把字段读取提到循环外,之后一直读寄存器里的旧值。解释执行阶段每次重新读主存,所以前几秒正常。

「JIT 编译优化」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。别只看报错的最后一行,往上看几行,真正的起因通常在上面。

具体做法是,把标志位声明成 volatile 保证每次读取都走主存;或者用 AtomicBoolean 并调用 get;检查所有跨线程共享的可变字段 别只修这一处;用 -XX:-TieredCompilation 复现一次确认与 JIT 有关。

验收就看一点,停止标志置位后线程在一秒内退出循环 不再出现只在一台机器上卡住的现象。

作者 9823189