假设我下节课:
public class Singleton{
private static Singleton _instance;
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null)
_instance = new Singleton();
}
}
return _instance;
}例如,我们有两个线程A和B,它们试图同时执行getInstance()方法。我是否正确地理解了这个过程:
发布于 2015-05-09 20:55:23
线程B在同步过程中被阻塞,当它继续执行时,它将看到已更改的字段_instance != null,因此它不会构造字段,而是返回现有的字段。
稍后来的所有其他线程都会看到实例正在设置,甚至不会锁定。
问题:您的代码是不完整的,您需要使用易失性来确保没有经过同步(希望是其中大多数)的线程仍然只看到一个完全发布的单例对象。
Java内存模型只保证初始化最后的字段。对于所有其他人,您需要一个安全发布,这可以通过以下方法实现:
避免易失性(或原子引用也可以安全地将对象发布到其他线程)的最简单方法是使用正常的对象初始化,这是JVM提供的有效和健壮的单例(但不是惰性):
class Singleton
{
private static final Singleton HIGHLANDER = new Singleton();
private Singleton() { } // not accessible
public static getSingleton() { return HIGHLANDER; }
}JDK在内部与“持物”物品一起使用类似的构造来实现相同的简单和健壮的模式,但以一种惰性的方式实现:
class Singleton
{
private Singleton() { } // not accessible
private static Class LazyHolder {
private static final Singleton LAZY_HIGHLANDER = new Singleton();
}
public static Singleton getInstance() {
return LazyHolder.LAZY_HIGHLANDER;
}
}这两种方法都不需要volatile变量访问(在DCL情况下需要)或同步(这是由JVM隐式完成的,它通过类锁保护初始化)。
发布于 2015-05-09 20:38:47
你在这里展示的东西叫做二次检查锁定。
静态变量属于类,而不是线程。两个线程都会看到正确的值,但是编译器可能会优化读取,这样静态变量就不会同时被选中。出于这个原因,关键字。
请注意,在版本5之前的Java版本中,即使使用易失变量,也可能无法正确工作。过去,赋值可以将部分构造的对象分配给变量。现在,构造函数必须返回才能继续分配。这将在任何现代版本的Java中正确工作。
发布于 2015-05-09 20:34:10
据我所知可能存在两个问题。线程B可能看到也可能看不到最新的值。线程B可能会看到一个部分构造的对象,如果构造函数做了很多事情,JVM决定改变代码的顺序。
使其易失性解决了这两个问题,因为它在关系之前强制执行,并阻止JVM重新排序代码执行,并更新其他线程中的值。
https://stackoverflow.com/questions/30144683
复制相似问题