Замок с двойной проверкой Артикул
-
03-07-2019 - |
Вопрос
я читал Эта статья по поводу «Двойной проверки блокировки» и вне основной темы статьи мне стало интересно, почему в каком-то месте статьи автор использует следующую идиому:
Листинг 7.Попытка решить проблему неправильной записи
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; }
И мой вопрос:Есть ли смысл дважды синхронизировать какой-то код с одной и той же блокировкой?Имеет ли это какую-то цель?
Спасибо заранее.
Решение
Смысл двойной блокировки заключался в том, чтобы пытаться чтобы предотвратить неправильную запись.Модель памяти определяет, где может происходить переупорядочение, частично с точки зрения блокировок.Замок гарантирует, что никакие записи (включая каких -либо в пределах синглтонского конструктора), по -видимому, происходит после «экземпляра = inst;»; линия.
Однако, чтобы углубиться в тему, я бы порекомендовал Статья Билла Пью.И никогда не пытайтесь это сделать :)
Другие советы
В статье речь идет о модели памяти Java до версии 5.0 (JMM).В этой модели оставление синхронизированного блока приводит к принудительной записи в основную память.Таким образом, это похоже на попытку убедиться, что объект Singleton вытеснен перед ссылкой на него.Однако это не совсем работает, потому что запись в экземпляр можно переместить вверх в блок — мотель с тараканами.
Однако модель до 5.0 так и не была реализована правильно.1.4 должна следовать модели 5.0.Классы инициализируются лениво, поэтому вы можете просто написать
public static final Singleton instance = new Singleton();
Или, что еще лучше, не используйте синглтоны, потому что они злые.
Джон Скит прав:читать Билл Пью статья.Идиома, которую использует Ганс, представляет собой точную форму, в которой не сработает, и не должен использоваться.
Это небезопасно:
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
Это также небезопасно:
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;
}
Никогда не делайте ни того, ни другого.
Вместо этого синхронизируйте весь метод:
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
Если вы не извлекаете этот объект миллион раз в секунду, снижение производительности в реальном выражении будет незначительным.
Я рассказываю об этом здесь:
http://tech.puredanger.com/2007/06/15/double-checked-locking/
После Джон Скит Рекомендация:
Однако, чтобы углубиться в предмет, я бы порекомендовал статью Билла Пью.А потом никогда не пытайся :)
А вот ключ для второго блока синхронизации:
Этот код ставит строительство вспомогательного объекта внутри внутреннего синхронизированного блока.Интуитивно понятная идея здесь заключается в том, что в точке синхронизации должен быть барьер памяти, и это должно предотвратить переупорядочение инициализации объекта -вспомогательного и назначения полезному вспомогательному.
По сути, с помощью внутреннего блока синхронизации мы пытаемся «обмануть» JMM, создав экземпляр внутри блока синхронизации, чтобы заставить JMM выполнить это распределение до завершения блока синхронизации.Но проблема здесь в том, что JMM возглавляет нас и перемещает задание, которое находится перед блоком синхронизации, внутрь блока синхронизации, возвращая нашу проблему обратно в начало.
Вот что я понял из этих статей, действительно интересно и еще раз спасибо за ответы.
Хорошо, но в статье сказано, что
Код в листинге 7 не работает из-за текущего определения модели памяти.Спецификация языка Java (JLS) требует, чтобы код внутри синхронизированного блока не перемещался из синхронизированного блока.Однако здесь не говорится, что код, не находящийся в синхронизированном блоке, не может быть перемещен в синхронизированный блок.
А также кажется, что JVM выполняет следующий перевод в «псевдокод» в ASM:
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 = new Singleton(); } //instance = inst; //5 } } } return instance; }
До сих пор не достигнута точка отсутствия записи после «instance=inst»?
Сейчас прочитаю статью, спасибо за ссылку.
Начиная с Java 5, вы можете обеспечить работу блокировки с двойной проверкой, объявив поле изменчивым.
Видеть http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html для полного объяснения.
По поводу этой идиомы есть очень полезная и разъясняющая статья:
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1
С другой стороны, я думаю, что dhighwayman.myopenid означает то, почему автор поместил один синхронизированный блок, ссылающийся на тот же класс (synchronized(Singleton.class)) в другой синхронизированный блок, ссылающийся на тот же класс.Это может произойти, когда внутри этого блока создается новый экземпляр (Singleton inst = экземпляр;), и чтобы гарантировать его поточно-ориентированность, необходимо написать еще один синхронизированный.
В противном случае я не вижу никакого смысла.
См. Google Tech Talk на Модель памяти Java за действительно хорошее введение в тонкости JMM.Поскольку здесь его нет, я также хотел бы отметить блог Джереми Мэнсона. «Параллелизм Java» особеннопост на Блокировка с двойной проверкой (Кажется, у любого, кто хоть что-то знает в мире Java, есть статья об этом :).
Для Java 5 и выше на самом деле существует вариант с двойной проверкой, который может быть лучше, чем синхронизация всего метода доступа.Об этом также говорится в Дважды проверенное объявление блокировки :
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}
Ключевым отличием здесь является использование изменчивый в объявлении переменной - иначе это не работает, и во всяком случае не работает в Java 1.4 или ниже.