我有以下代码:
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 Singleton声明服务,因此该类始终只有一个实例。
答案 0 :(得分:2)
有多个线程并行调用代码的理论可能性;并且由于不使用volatile,线程A没有看到线程B所做的更新。我自己从未遇到过这样的行为,但是:它是可能的,当它发生时,非常奇怪的错误可能有两个相同的单身实例。
您可以研究此SEI cert site,以便对该主题进行全面讨论。
答案 1 :(得分:1)
我不明白为什么你需要让这个变化。您在示例中使用双重检查锁定解决方案。接受的答案中的link也表明这符合要求。
所以接受的答案实际上是错误的,你不需要挥发性的。但是,这种初始化最干净的解决方案是“初始化按需持有者类成语”,它也在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
“初始化按需持有人类成语”是最快的。
答案 2 :(得分:0)
有问题的代码不是线程安全的,因为它在公共方法中有一个检查然后处理问题。这个问题的原因是共享的可变状态在类中没有正确地同步。这是一个解决方案:
public class EntityManagerFactory implements ....{
private static class EntityManagerHolder {
public static EntityManager entityManager = new EntityManager();
}
public static EntityManager getEntityManager(){
return EntityManagerHolder.entityManager;
}
}
请注意,此处没有synchronized关键字。这是因为entityManager在holder类中被初始化为static,这意味着它在类加载之后但在任何线程可以启动之前加载,因此不需要同步化。
holder类的唯一目的是通过使用JVM延迟类加载策略来防止急切的初始化成本。 holder类仅在实际需要时初始化,而不是在类加载时进行初始化。
Bottomline:你可以避免同步和急切的初始化,并一举获得一个单身。 ;)