4

首先:是的,我知道在 Java 中做单例的最好方法是使用enums,但是如果由于某种原因你需要子类化一个单例类,你不能使用枚举,所以......

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 可以在单例引用被分配之后但在单例初始化之前被抢占,因此线程 2 可以返回对未初始化单例实例的引用。

我的问题:以下更改是否可以解决该问题?我已经开始阅读 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; 
} 
4

3 回答 3

2
于 2013-10-09T05:51:00.280 回答
1

The answer depends on optimization which can be applied for the second code by compiler (it means that second one can be transformed to first one by compiler). You can write the code using AtomicReference which will allow to avoid the problem:

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(); 
} 
于 2013-10-09T06:18:44.423 回答
0

Deleted as wrong, keeping the empty answer for its discussions. Wow, live and learn!

于 2013-10-09T05:53:47.393 回答