Вопрос

Java 5 и выше только. Предположим, что многопроцессорный компьютерный компьютер (вы, вероятно, используете один прямо сейчас).

Вот код для ленивой инициализации синглтона:

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

Делает instance должны быть объявлены volatile Чтобы предотвратить перезапись оптимизатора () следующим образом () следующим образом (что было бы правильно в последовательной программе):

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

Предполагая, что оптимизатор не переписывает код, если instance не объявлен volatile это все еще гарантированно промыты в память, когда synchronized блок выходит и читается из памяти, когда synchronized Блок вводится?

Редактировать: я забыл сделать GetSstance () статическими. Я не думаю, что меняет действительность ответов; Вы все знали, что я имел в виду.

Это было полезно?

Решение

Да, instance должен быть объявлен volatile. Отказ Даже тогда рекомендуется не использовать двойной проверку. Это (или быть точным, модель памяти Java), используемая для серьезного недостатка, которая разрешена публикация частично реализованных объектов. Это было зафиксировано в Java5, все еще DCL является устаревшей идиомой и больше не нужно его использовать - используйте Держатель ленивого инициализации IDIOM вместо.

От Java параллелизм на практике, Раздел 16.2:

Настоящая проблема с DCL является предположение, что худшее, что может произойти при чтении общего эталона без синхронизации - это ошибочно видеть устаревшее значение (в этом случае, null); В этом случае DCL IDIOM компенсирует этот риск, пытаясь снова с замком. Но худший случай на самом деле значительно хуже - можно увидеть текущее значение эталона, но устаревшие значения для состояния объекта, что означает, что объект можно увидеть в неверном или неправильном состоянии.

Последующие изменения в JMM (Java 5.0 и позже) включили DCL для работы если resource сделан volatile, и воздействие производительности это мало, так как volatile Читает, как правило, чуть дороже, чем нетолюбильные читания. Тем не менее, это идиома, утилита которой в значительной степени проходила - силы, которые мотивировали его (медленная недопустимая синхронизация, медленный запуск JVM) больше не в игре, что делает его менее эффективным в качестве оптимизации. Держатель ленивого инициализации IDIOM предлагает те же преимущества и легче понять.

Другие советы

Да, экземпляр должен быть волатильный, используя двойной проверенной блокировки в Java, потому что инициализация mysingleton может разоблачить частично построенный объект для остальной части системы. Также верно, что потоки синхронизируются, когда они достигают «синхронизированного» оператора, но это слишком поздно в этом случае.

Википедия И пару других вопросов переполнения стека есть хорошие дискуссии о «двойной проверке блокировки», поэтому я советую прочитать об этом. Я также советую не использовать его, если профилирование не показывает реальную потребность в производительности в этом конкретном коде.

Существует безопасная и читаемая идиома для ленивой инициализации Java Singletons:

class Singleton {

  private static class Holder {
    static final Singleton instance = create();
  }

  static Singleton getInstance() { 
    return Holder.instance; 
  }

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

Если вы используете более подробный двухместный заблокированный шаблон, вы должны объявить поле volatile, как и другие уже отмечали.

Нет, это не должно быть нестабильным. Видеть Как решить «двойное заблокированное блокировка» сломана «Декларация» в Java?

Предыдущие попытки все потерпели неудачу, потому что если вы сможете обмануть Java и избежать волатильной / синхронизации, Java может обмануть вас и дать вам неверный вид на объект. Однако новый final семантика решает проблему. Если вы получите ссылку на объект (обыденным чтением), вы можете безопасно прочитать его final поля.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top