Java内存模型中提到:当线程在释放相关监视器的过程中退出同步块时,JMM要求将本地处理器缓存刷新到主内存。类似地,作为进入同步块时获取监视器的一部分,本地缓存将无效,因此后续读取将直接进入主内存,而不是本地缓存
那么,为什么在那个代码中我必须声明实例为volatile,因为当第二个线程进入synch块时,它将直接进入主存??
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
易失性解决了这个问题,因为JDK内存规范(从JDK5开始)保证了对非易失性对象的写入相对于对易失性的对象的写入不会出现无序。因此,任何看到instance != NULL
的线程都必须看到实例本身。
您必须声明它为volatile,否则无法保证对getInstance()的两个调用将返回相同的实例。
不能保证主内存会被访问,只有一个缓存一致的值。即所有线程将看到相同的值。
BTW:你当然知道它比需要的要复杂得多。你只需要
public enum MySingleton {
INSTANCE;
}
做同样的事情。