-
13-12-2019 - |
题
Java内存模型中提到:当线程退出同步块作为释放关联监视器的一部分时,JMM 要求将本地处理器缓存刷新到主内存。相似地, 作为进入同步块时获取监视器的一部分,本地缓存将失效,以便后续读取将直接进入主内存而不是本地缓存。
那么为什么在该代码中我必须将实例声明为易失性,因为当第二个线程进入同步块时将直接进入主内存?
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
我的意思是,当另一个线程进入同步块并进行第二次检查时,它应该从主内存中更新,如上所述。
解决方案
竞争条件是这样的:
线程A看到
instance == NULL
并正在运行这段代码instance = new MySingleton();
. 。写入到instance
是可见的,但写入MySingleton
还没有。线程B看到
instance != NULL
并开始处理实例。线程 B 现在正在处理一个它看不到其构造的对象。
制作 instance
易失性解决了这个问题,因为从 JDK5 开始,JDK 内存规范保证对非易失性对象的写入相对于对易失性对象的写入不会出现乱序。所以任何看到的线程 instance != NULL
必须看到实例本身。
其他提示
您必须声明它volatile,否则无法保证对getInstance()的两个调用将返回相同的实例。
无法保证将访问主内存,只有缓存一致值。即,所有线程都会看到相同的值。
btw:你知道课程比需要更复杂。你需要的只是
public enum MySingleton {
INSTANCE;
}
.
做得太多了。
不隶属于 StackOverflow