在Simple Singleton实例中获取ExceptionInInitializerError

时间:2010-10-21 20:58:28

标签: java exception static singleton initializer

我必须做一些非常愚蠢的事情,但是当我尝试在我的Singleton中实例化一个对象时,我得到一个ExceptionInInitializerError:

class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  // Do I instantiate obj here???
  private MySingleton() {
    //obj = new OtherObject();
  }

  // Or here?
  {
    //obj = new OtherObject();
  }

  // Or where? ...

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

我应该在构造函数中创建另一个对象,还是对于单例来说总是应该为空?我在构造函数和初始化程序块中都得到了异常......

这是main():

public static void main(String[] args) {
    try {
        MySingleton singleton = MySingleton.getInstance();
    } catch (Error e) {
        System.err.println("Error!");
    }
}

6 个答案:

答案 0 :(得分:5)

您可以通过消除lazy initialization风险(您目前在例外中支付的风险)来避免一些混淆。由于您实际上只是返回静态实例,为什么不在运行时创建该静态实例(即,不要等待null):

class MySingleton {

  private static MySingleton instance = new MySingleton();

  // You could do this here or in the constructor
  // private OtherObject obj = new OtherObject();

  /** Use this if you want to do it in the constructor instead. */
  private OtherObject obj;

  private MySingleton() {
      obj = new OtherObject();
  }

  /** Now you can just return your static reference */
  public static MySingleton getInstance() {
      return instance;
  }

}

如果你注意到,现在一切都是确定性的和直截了当的。您的MySingleton.instance在运行时填充,可通过静态方法MySingleton.getInstance()访问。

我意识到这与原始GOF设计模式书的确切模式不符,但您会注意到该类的用法实际上是相同的。

编辑:跟进其他答案和评论中提出的一些线索安全点,我将尝试说明问题和GOF书中原始提议的解决方案是如何非线程安全。有关更好的参考,请参阅我在ACM / Safari在线书架上拥有和拥有的Java Concurrency in Practice。坦率地说,这比我描绘的例子更好,但是,嘿,我们可以努力....

因此,假设我们有两个名为Thread1和Thread2的线程,巧合的是,每个线程同时命中MySingleton.getInstance()方法。这在现代多核超线程系统中是完全可能的。现在,即使这两个线程同时碰到了这个方法的入口点,也无法确定哪个线程会触及任何特定的声明。所以,你可以看到这样的东西:

// Note that instance == null as we've never called the lazy getInstance() before.

Thread1: if (instance == null)
Thread2: if (instance == null) // both tests pass - DANGER
Thread1:     instance = new MySingleton();
Thread2:     instance = new MySingleton(); // Note - you just called the constructor twice
Thread1: return instance; // Two singletons were actually created 
Thread2: return instance; // Any side-effects in the constructor were called twice

if-null测试中说明的问题称为race condition。你什么时候不知道是什么声明。结果,就像两条线都相互竞争到了悬崖。

如果你看看我的例子同样的检查,两个线程仍在同一时刻点击getInstance(),你会看到类似这样的事情:

Thread1: return instance;
Thread2: return instance;

简单,是的,但这是我的观点。该对象很久以前构造了一次,并且线程可以指望它的值是一致的。没有种族。

注意:您仍然需要担心OtherObject的构造函数的内容。那里的教训是:并发性很难。如果您正在使构造函数线程安全(您应该这样做),请确保OtherObject的作者为您提供相同的礼貌。

答案 1 :(得分:2)

答案很简单,不要使用单身人士。虽然GoF书有一些好主意,但单身模式不是其中之一。

正如其他人所说的那样,正确安全地使用单例是非常棘手的,参见

IBM article on thread safe singletons

实际上,所有Singleton实现都是一个全局对象。这很糟糕,它污染了命名空间。如果不是不可能的话,它使得适当的单元测试(Junit等)变得更加困难。你永远不需要它。一个简单的静态工厂类是更清晰的代码,避免了全局污染并且代码更少。

答案 2 :(得分:1)

您应该在构造函数中实例化OtherObj。给出错误的确切代码是什么?

编辑 - 以下为我工作

class MySingleton {

  private static MySingleton instance = null;
  private Integer obj;


  private MySingleton() {
    obj = new Integer(2);
  }


  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

然后我只是从主循环中调用getInstance。你可能想看看

http://en.wikipedia.org/wiki/Singleton_pattern#Java

答案 3 :(得分:1)

class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  private MySingleton() {
      obj = new OtherObject();
  }

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

答案 4 :(得分:1)

静态初始化部分用于初始化标记为static的类的成员。由于OtherObject obj不是静态的,因此不应该在那里进行初始化。正确的位置是在构造函数中初始化它。

如果在构造函数中有obj = new OtherObject()时遇到ExceptionInInitializerError,问题可能出在另一个类(也许是OtherObject?)上,错误地初始化静态成员。

答案 5 :(得分:0)

你的Singleton类在这里没有错。我猜想在OtherObject类的构造函数中有一个问题。

正确的语法是:

class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  private MySingleton() {
    obj = new OtherObject();
  }

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

现在,您只需要向我们提供有关OtherObject的更多信息: - )