JMM是否保证synchronized写入对其他线程同步块后读取的变量可见?
JMM中synchronized块内写入的变量,对其他线程同步块后读取的可见性问题
我想确认JMM是否保证:一个线程在synchronized块内对变量的写入,对另一个线程在同一个监视器的synchronized块执行完成后读取对应变量的操作,具有可见性?
初始疑问示例代码
public class SynchronizedWriteRead { private int a; private int b; public void writer() { synchronized (this) { a = 5; b = 7; } } public void reader() { synchronized (this) { int r1 = a; /* 确定能读到5 */ } int r2 = b; /* 是否能保证读到7? */ } }
对JMM规则的困惑
JMM明确规定,对一个监视器的解锁操作happens-before后续对该监视器的所有加锁操作,但我不确定这个规则是否仅局限于synchronized块内部的读写操作。
近期看到Aleksey Shipilëv的文章《Safe Publication and Safe Initialization in Java》中的描述:
注意在不安全的双重检查锁定(Unsafe DCL)中使用synchronized并无帮助,与外行认为它能神奇“刷新缓存”的想法相反。若读取受保护状态时没有配对的锁,则无法保证能看到锁保护的写入之前的操作。
正是这段内容让我产生了上述疑问,且我暂时没在JLS中找到明确答案。
对比volatile的可行方案
换个思路,我们可以借助volatile的happens-before保证实现类似的可见性效果,示例代码如下:
public class VolatileHappensBefore { private int a; /* 明确非volatile */ private volatile int b; public void writer() { a = 5; b = 7; } public void reader() { int r1 = b; /* 能保证读到7 */ int r2 = a; /* 能保证读到5 */ } }
这里的逻辑是:同一线程内的顺序操作满足happens-before,再加上happens-before的传递性,就能保证reader线程读取b之后,一定能看到writer线程对a的写入。
进一步的探索:借助synchronized的传递性?
那我们能不能用同样的逻辑,借助synchronized的happens-before保证实现类似效果?比如下面的代码(添加sync变量避免JVM优化移除空同步块):
public class SyncHappensBeforeTest { private int a; private int b; private int sync; // 用于避免空同步块被JVM移除 public void writer() { a = 5; b = 7; synchronized (this) { sync = 1; } } public void reader() { synchronized (this) { int r = sync; } int r1 = a; /* 是否能保证读到5? */ int r2 = b; /* 是否能保证读到7? */ } }
内容的提问来源于stack exchange,提问作者TwITe
相关产品推荐
相关产品推荐

