java中的单例模式。懒惰的初始化

时间:2010-03-26 08:29:44

标签: java static synchronization singleton thread-safety

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

1.上述getInstance方法的实现存在缺陷吗? 2.这两种实现有什么区别。?

public static synchronized MySingleton getInstance() { 
 if (_instance==null) {
  _instance = new MySingleton();
 }

 return _instance;
} 

我已经在stackoverflow中看到了很多关于单例模式的答案,但我发布的问题主要是知道在这种特殊情况下方法和块级别的'同步'的区别。

6 个答案:

答案 0 :(得分:27)

  

1.上述getInstance实现存在缺陷   方法

它不起作用。您最终可能会遇到Singleton的几个实例。

  

2.两种实现之间有什么区别。?

第二个工作,但需要同步,当你从不同的线程访问该方法时,这可能会减慢系统的速度。

最直接的正确实施:

public class MySingleton{
    private static final MySingleton _instance = new MySingleton();
    private MySingleton(){}
    public static MySingleton getInstance() { 
        return _instance;
    }
}

更短更好(安全可序列化):

public enum MySingleton{
    INSTANCE;

    // methods go here
}

单身人士的懒惰初始化是一个受到关注方式与其实际实用性不成比例的话题(IMO争论双重检查锁定的复杂性,你的例子是第一步,只不过是一场小便大赛。)

在99%的情况下,您根本不需要延迟初始化,或者Java首次引用类时的“初始化”就足够了。在剩下的1%的情况下,这是最好的解决方案:

public enum MySingleton{
    private MySingleton(){}
    private static class Holder {
         static final MySingleton instance = new MySingleton();
    }
    static MySingleton getInstance() { return Holder.instance; }
}

答案 1 :(得分:5)

  

1.上述getInstance实现存在缺陷   方法

是的,synchronized关键字也应该包装if语句。如果不是那么两个或多个线程可能会通过创建代码。

  

2.两种实现之间有什么区别。?

第二种实现是正确的,从我的观点来看更容易理解。

在方法级别使用synchronized来静态方法在类上进行同步,即您在示例中所做的事情1.在方法级别使用synchronized实例方法在对象实例上进行同步。

答案 2 :(得分:4)

第一个有两个方面存在缺陷。正如其他人提到的那样,多线程可以通过

if (_instance==null) {

他们会等待对方,直到对象完全构建,但他们会进行实例化并替换变量中的引用。

第二个缺陷有点复杂。一个线程可以进入构造函数new MySingleton(),然后JVM切换到另一个线程。另一个线程可以检查变量是否为null,但是可能包含对部分构造的对象的引用。所以另一个线程适用于部分构造的Singleton,这也不好。所以应该避免第一个变种。

第二种变体应该可以正常工作。不要太在意效率,直到你明确地将其识别为阻挡剂。现代JVM可以优化掉不需要的同步,因此在实际的生产代码中,这种结构可能永远不会损害性能。

答案 3 :(得分:4)

Bob Lee在讨论了懒惰负载单例的各种方法 Lazy Loading Singletons和“正确”方法是Initialization on Demand Holder (IODH) idiom,它只需要很少的代码并且没有同步开销。

static class SingletonHolder {
  static Singleton instance = new Singleton();    
}

public static Singleton getInstance() {
  return SingletonHolder.instance;
}

Bob Lee还解释了他何时想要懒惰加载一个单身人士(在测试和开发期间)。老实说,我不相信会有很大的好处。

答案 4 :(得分:2)

第二个是线程安全的,但无论是否构造实例,它都会在每次调用时都有同步的开销。第一个选项有一个主要缺陷,它没有检查synchronized块中的if(_instance == null)以防止创建两个实例。

答案 5 :(得分:0)

我建议以下实施

public class MySingleTon
{

  private static MySingleton obj;

  //private Constructor
  private MySingleTon()
  {
  }


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