Java多线程纠错 - 线程安全单例

时间:2013-10-09 05:22:37

标签: java multithreading singleton

第一个关闭:是的,我知道在Java中使用单例的最佳方法是使用enum s,但如果由于某种原因需要子类化单例类,则不能使用枚举,所以......

JavaWorld的David Geary很久以前发表了一篇关于用Java实现单例的文章。他认为对线程安全的单例实现的以下优化是有问题的:

public static Singleton getInstance() 
{ 
    if (singleton == null) 
    { 
       synchronized(Singleton.class) 
       { 
           if(singleton == null) {
              singleton = new Singleton();
           } 
       }
    }
    return singleton; 
} 

(详见:http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html?page=4#sthash.G8lzWOfT.dpuf

Geary说这是'双重检查锁定'优化

  

无法保证正常工作,因为编译器可以自由分配   在单例之前的值为单例成员变量   构造函数被调用。如果发生这种情况,可以抢占线程1   在分配了单例引用之后,但在之前   singleton已初始化,因此Thread 2可以返回对a的引用   未初始化的单身实例。

我的问题:以下更改是否可以解决该问题?我已经开始阅读Goetz的Java并发书了,似乎允许编译器在线程内操作进行混乱,所以我不太自信......在我看来,singleton = temp;是一个原子操作,在这种情况下,我认为它应该。请解释一下。

public static Singleton getInstance() 
{ 
    if (singleton == null) 
    { 
       synchronized(Singleton.class) 
       { 
           if(singleton == null) {
              Singleton temp = new Singleton();
              singleton = temp;
           } 
       }
    }
    return singleton; 
} 

3 个答案:

答案 0 :(得分:2)

第二个代码与第一个代码顺序一致(它们在单个线程环境中严格相同),并且不会引入任何额外的内存同步点。

所以是的,编译器有权重写第二个代码并将其转换为第一个代码,这意味着它也是不安全的。

singleton = temp;是原子的事实在这里没有用。它只表示singleton为null或与temp保持相同的引用。但这并不妨碍temp / singleton指向“非构造”对象。

Java内存模型在发生前(HB)关系方面起作用。在两个代码中只有一个hb:同步块的退出hb后续进入该块。 if (singleton == null)singleton=…不共享任何关系,因此可以进行重新排序。

最重要的是,修复它的唯一方法是在两个语句之间引入一个hb:在同步块中移动if或者例如标记singleton volatile。

答案 1 :(得分:1)

答案取决于可以通过编译器应用于第二代码的优化(这意味着可以通过编译器将第二代码转换为第一代码)。您可以使用AtomicReference编写代码,以避免此问题:

private static AtomicReference<Singleton> singleton = new AtomicReference<Singleton>(null);
...
public static Singleton getInstance() 
{ 
    if (singleton.get() == null) 
    { 
       synchronized(Singleton.class) 
       { 
           if(singleton.get() == null) {
              singleton.compareAndSet(null, new Singleton());
           } 
       }
   }
   return singleton.get(); 
} 

答案 2 :(得分:0)

删除错误,为讨论保留空白答案。哇,生活和学习!