求Java构造器中this逃逸(最后语句)导致异常输出的示例程序
构造器中this逃逸导致未完全构造对象的可复现示例
你的原代码之所以总是输出42,是因为调用了t.join(),强制主线程等待子线程执行完毕才继续,子线程必然能看到构造器中已经完成的state=42赋值,无法复现未完全构造的问题。
以下是能大概率复现非预期输出的示例:
public class ThisEscapeDemo { private int state; public ThisEscapeDemo() { // 构造器内的模拟耗时操作,放大指令重排的概率 for (int i = 0; i < 10000; i++) { // 空循环占用CPU,给JVM指令重排留机会 } state = 42; // 构造器最后一步:this引用逃逸到新线程 new Thread(() -> { // 可能读取到state的默认初始值0,而非预期的42 System.out.println("线程读取到的state值:" + state); }).start(); } public static void main(String[] args) { // 多次运行实例,增加复现概率 for (int i = 0; i < 100; i++) { new ThisEscapeDemo(); } } }
为什么会出现非预期值?
- 无线程等待:构造器启动子线程后没有调用
join(),主线程会继续执行(甚至直接结束),子线程可能在构造器完成state=42赋值前就开始执行。 - JVM指令重排序:Java虚拟机允许在不违反单线程语义的前提下重排指令。构造器中
state=42的赋值操作,可能被重排到对象实例化(即this引用可用)之后,导致子线程拿到的是未完成赋值的对象,读取到state的默认值0。 - 模拟耗时操作:空循环的作用是占用CPU时间,让子线程有更多机会在
state赋值前调度执行,提升复现概率。
如果将state声明为volatile,会插入内存屏障阻止指令重排,就能避免这种问题,确保子线程看到的是已初始化的state值。
内容的提问来源于stack exchange,提问作者Otto
相关产品推荐
相关产品推荐

