使用 volatile 关键字创建一个实例

Using volatile keyword for creating one instance

我有以下代码:

public class EntityManagerFactoryProviderImpl implements EntityManagerFactoryProvider {

  private EntityManagerFactory entityManagerFactory=null;//line XXX

  public EntityManagerFactory getFactory(){
      if (entityManagerFactory==null){
          buildFactory();
      }
      return entityManagerFactory;
  }

  private synchronized void buildFactory(){
      if (entityManagerFactory!=null){
          return;
      }
      entityManagerFactory=...
  }
}

所以我需要 entityManagerFactory 实例只创建一次 - 当 getFactory() 第一次被调用时。

在这种情况下,我必须将第 XXX 行上的变量 entityManagerFactory 设置为可变的吗?

此外,EntityManagerFactoryProviderImpl 是一个 OSGI 单例声明式服务,因此此 class.

始终只有一个实例

理论上存在多个线程并行调用代码的可能性;并且由于 not 使用 volatile,线程 A 看不到线程 B made.I 自己从未遇到过这种行为的更新,但是 surr:这是可能的,当它发生时,具有相同单例的两个实例可能会产生非常奇怪的错误。

您可以研究此 SEI cert site 以获得对该主题的完整讨论。

我不明白你为什么需要让它变得不稳定。您在示例中使用双重检查锁定解决方案。接受的答案中的 link 也表明这是合规的。

所以接受的答案实际上是错误的,你不需要volatile。 Howeer,这种初始化的最干净的解决方案是 "initialize-on-Demand Holder Class Idiom" ,它也在 link.

更新我错了。双重检查锁定可能会失败,因为 EntityManagerFactory 对象可以在部分构造的状态下看到,只有该对象中的最终字段才能保证看到。 http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5-110

中明确提到了这一点
  int j = f.y;  // could see 0

"initialize-on-Demand Holder Class Idiom"最快。

有问题的代码不是线程安全的,因为它在 public 方法中有检查然后执行问题。这个问题的原因是 class 中共享的可变状态没有正确同步。这是一个解决方案:

public class EntityManagerFactory implements ....{

private static class EntityManagerHolder {
  public static EntityManager entityManager = new EntityManager();
}

public static EntityManager getEntityManager(){
  return EntityManagerHolder.entityManager;
}

}

注意这里没有同步关键字。这是因为 entityManager 在 holder class 中被初始化为静态,这意味着它在 class 加载之后但在任何线程可以启动之前加载,因此不需要同步。

持有者 class 的唯一目的是通过使用 JVM 惰性 class 加载策略来防止急切的初始化成本。持有者 class 仅在实际需要时进行初始化,而不是在 class 加载时进行急切的静态初始化。

底线:您避免了同步和急切的初始化,并一口气获得了单例。 ;)