使用 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 加载时进行急切的静态初始化。
底线:您避免了同步和急切的初始化,并一口气获得了单例。 ;)
我有以下代码:
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 加载时进行急切的静态初始化。
底线:您避免了同步和急切的初始化,并一口气获得了单例。 ;)