обновить память перед синхронизацией? - PullRequest
0 голосов
/ 13 марта 2012

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

так почему в этом коде я должен объявить экземпляр как volatile, так как, когда второй поток входит в блок синхронизации, он перейдет непосредственно в основную память ??

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;
  }
}

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

Ответы [ 2 ]

4 голосов
/ 13 марта 2012

Состояние гонки:

  1. Поток A видит instance == NULL и выполняет этот код instance = new MySingleton();.Запись в instance видима, но запись в MySingleton еще не сделана.

  2. Поток B видит instance != NULL и начинает работать с экземпляром.

  3. Поток B теперь работает с объектом, конструкцию которого он не видит.

Создание instance volatile решает проблему, поскольку спецификация памяти JDK, начиная с JDK5, гарантируетзапись в энергонезависимые объекты не будет рассматриваться не по порядку относительно записи в энергозависимый объект.Поэтому любой поток, который видит instance != NULL, должен видеть сам экземпляр.

1 голос
/ 13 марта 2012

Вы должны объявить его энергозависимым, иначе нет гарантии, что два вызова getInstance () вернут один и тот же экземпляр.

Нет гарантии, что к основной памяти будет получен доступ, только согласованное с кэшем значение,то есть все потоки будут видеть одно и то же значение.

Кстати: вы, конечно, знаете, что оно намного сложнее, чем нужно.Все, что вам нужно, это

public enum MySingleton {
     INSTANCE;
}

делает то же самое.

...