0

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;
  }
}

我的意思是当另一个线程进入同步块并进行第二次检查时,它应该如上所述从主内存更新。

4

2 回答 2

4

比赛条件是这样的:

  1. 线程 A 看到instance == NULL并正在运行此代码instance = new MySingleton();。写入instance可见,但写入MySingleton尚不可见。

  2. 线程 B 看到instance != NULL并开始处理实例。

  3. 线程 B 现在正在处理一个它看不到构造的对象。

使instancevolatile 解决了这个问题,因为从 JDK5 开始,JDK 内存规范保证了对非易失性对象的写入相对于对易失性对象的写入不会出现乱序。所以任何看到instance != NULL的线程都必须看到实例本身。

于 2012-03-13T09:45:21.040 回答
1

您必须将其声明为 volatile,否则无法保证对 getInstance() 的两次调用将返回相同的实例。

不能保证会访问主内存,只有缓存一致的值。即所有线程将看到相同的值。

顺便说一句:你当然知道它比需要的复杂得多。所有你需要的是

public enum MySingleton {
     INSTANCE;
}

做同样的事情。

于 2012-03-13T09:39:47.670 回答