Синхронизированное ключевое слово Java очищает кеш? - PullRequest
6 голосов
/ 30 августа 2010

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, чтобы оптимизатор не переписывал getInstance () следующим образом (что было бы правильно в последовательной программе):

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 введено?

РЕДАКТИРОВАТЬ: я забыл сделать статический getInstance (). Я не думаю, что это меняет достоверность ответов; Вы все знали, что я имел в виду.

Ответы [ 5 ]

15 голосов
/ 30 августа 2010

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

С Параллелизм Java вПрактика , раздел 16.2:

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

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

1 голос
/ 30 августа 2010

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

class Singleton {

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

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

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

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

1 голос
/ 30 августа 2010

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

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

0 голосов
/ 31 августа 2010

Нет.Это не должно быть изменчивым.См. Как решить декларацию «Двойная проверка блокировки сломана» в Java?

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

0 голосов
/ 30 августа 2010
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...