Внутренняя синхронизация на том же объекте, что и внешняя синхронизация - PullRequest
6 голосов
/ 17 марта 2010

Недавно я посетил лекцию о некоторых шаблонах проектирования:

Был отображен следующий код:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          inst = new Singleton();        //4
        }
        instance = inst;                 //5
      }
    }
  }
  return instance;
}

взято из: Двойная проверка блокировки: взять два

Мой вопрос не имеет ничего общего с вышеупомянутым шаблоном, но с синхронизированными блоками:

Есть ли какая-либо польза от двойной синхронизации, выполняемой в строках 1 и 3, в связи с тем, что операция синхронизации выполняется для того же объекта ?

Ответы [ 3 ]

11 голосов
/ 17 марта 2010

В старой модели памяти Java (JMM) выход из блока synchronized предположительно сбрасывал локальные данные в основную память.Ввод блока synchronized, используемого для перечитывания кэшированных данных.(Здесь кэш содержит регистры с соответствующими оптимизациями компилятора.) Старый JMM был сломан и неправильно реализован.

В новом JMM он ничего не делает.Новый JMM определен для 1.5 и реализован для 1.4 Sun JRE.1.5 завершил свой период окончания срока службы некоторое время назад, поэтому вам не нужно беспокоиться о старом JMM (ну, возможно, Java ME сделает что-то непредсказуемое).

2 голосов
/ 24 марта 2010

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

Например

public void doSomething()
{
  synchronized(this) { // "this" locked
    methodCall1();
    synchronized(this) {
      methodCall2();
    } // memory flushed
    methodCall3();
  } // "this" unlocked and memory flushed
}

Может быть скомпилировано для выполнения в этом порядке

public void doSomething()
{
  synchronized(this) { // "this" locked
    methodCall1();
    synchronized(this) {
      methodCall2();
      methodCall3();
    } // memory flushed
  } // "this" unlocked and memory flushed
}

Для более подробного объяснения посмотрите Двойная проверка блокировки в Исправление, которое не работает раздел примерно на треть пути вниз.

2 голосов
/ 17 марта 2010

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

Подробности вы найдете в Модель памяти Java

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