Получение ExceptionInInitializerError в простой реализации Singleton - PullRequest
1 голос
/ 22 октября 2010

Я, должно быть, делаю что-то очень глупое, но я получаю 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 ]

5 голосов
/ 22 октября 2010

Вы можете избежать некоторой путаницы, устраняя риск lazy initialization (за который вы в настоящее время платите за исключение).Поскольку вы действительно просто возвращаете свой статический экземпляр, почему бы просто не создать этот статический экземпляр во время выполнения (т. Е. Не ждать нулевое значение):

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 не является потокобезопасным.Для получения более подробной информации см. Параллелизм Java на практике , который у меня есть и есть на моей книжной полке ACM / Safari.Это, откровенно говоря, лучший источник, чем мой набросок, но, эй, мы можем только стремиться ...

Итак, представьте, что у нас есть два потока с именами 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, называется условие гонки .Вы не можете знать, кто и на какое заявление пойдет.В результате, как будто оба потока гоняют друг друга к обрыву.

Если вы посмотрите на тот же анализ моего примера, когда оба потока все еще нажимают на getInstance () в одно и то же время, вы видите что-товот так:

Thread1: return instance;
Thread2: return instance;

Упрощенно, да, но это моя точка зрения.Объект был создан один раз, давным-давно, и потоки могут рассчитывать на его согласованность значений.Расы не существует.

ПРИМЕЧАНИЕ: вам все равно придется беспокоиться о содержимом конструктора для OtherObject.Урок: параллелизм труден.Если вы делаете свой конструктор потоком безопасным (что и нужно), убедитесь, что автор OtherObject проявил к вам ту же любезность.

2 голосов
/ 22 октября 2010

Ответ прост, не используйте Singletons.В то время как в книге GoF есть несколько хороших идей, шаблон синглтона не является одним из них.

Как уже говорили другие, правильное использование потоковых синглетов сложно, см.

Статья IBM о поточно-ориентированных синглетонах

В действительности все реализации Singleton являются глобальным объектом.Это плохо, это загрязняет пространство имен.Это делает правильное юнит-тестирование (Junit и т. Д.) Намного сложнее, если не невозможно.Тебе это не нужно, никогда.Простой статический фабричный класс - это более чистый код, он предотвращает глобальное загрязнение и меньше кода.

1 голос
/ 22 октября 2010

Статические разделы инициализации предназначены для инициализации членов класса, помеченных как статические.Поскольку OtherObject obj не является статическим, его не следует инициализировать там.Правильное место - инициализировать его в конструкторе.

Если вы получаете ExceptionInInitializerError, когда в конструкторе obj = new OtherObject(), проблема может быть в другом классе (возможно, OtherObject?), Который неправильно инициализирует staticчлены.

1 голос
/ 22 октября 2010
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;
  }

}
1 голос
/ 22 октября 2010

вы должны создать экземпляр OtherObj в своем конструкторе.Какой точный код дает ошибку?

edit - у меня сработало следующее

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

0 голосов
/ 22 октября 2010

Ваш синглтон-класс здесь не виноват. Я предположил бы проблему в конструкторе класса 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: -)

...