Java Memory Model 意外行为:求教JSR-133规范中该类合法表现的原理
你所贴的示例是JMM领域的经典测试用例,场景为两个共享变量a、b初始值均为0,存在两个执行线程:
- 线程1执行操作:
a = 1→x = b- 线程2执行操作:
b = 1→y = a
这类运行结果被JSR-133规范允许的核心原因有两个:
1. 合法的指令重排序优化
JMM的设计原则是,只要不改变单线程环境下的程序执行结果,编译器、CPU都可以对指令序列做重排序优化。
对于线程1来说,a=1和x=b两个操作不存在数据依赖,重排序两者的执行顺序不会影响线程1单独运行的结果,因此完全允许被调整为x = b先执行,a = 1后执行;同理线程2的操作也可以被重排序为y = a先执行,b = 1后执行。
这种情况下的执行时序可能为:
- 线程1执行
x = b,此时b还未被赋值,x取初始值0 - 线程2执行
y = a,此时a还未被赋值,y取初始值0 - 线程1执行
a = 1 - 线程2执行
b = 1
最终就会出现x=0且y=0的结果。
2. 工作内存与主内存的可见性延迟
就算没有发生指令重排序,JMM定义的内存模型中,每个线程都有独立的工作内存,线程对变量的修改会先写入自己的工作内存,默认不会立即同步到主内存,也不会主动推送更新给其他线程的工作内存。
这种情况下的执行时序可能为:
- 线程1执行
a = 1,修改仅存在于线程1的工作内存中,未同步到主内存 - 线程2执行
b = 1,修改仅存在于线程2的工作内存中,未同步到主内存 - 线程1执行
x = b,从主内存读取b的值,拿到初始值0 - 线程2执行
y = a,从主内存读取a的值,拿到初始值0
最终同样会得到x=0且y=0的结果。
JSR-133允许这类行为的根本目的是为编译器、CPU留下足够的优化空间,提升程序的整体运行性能。如果要禁止这类结果出现,只需要给a、b变量加上volatile修饰符,就可以禁用相关指令重排序,同时保证变量修改的跨线程可见性,避免该问题。
内容的提问来源于stack exchange,提问作者Hlib
相关产品推荐
相关产品推荐

