Вопрос

я читал Эта статья по поводу «Двойной проверки блокировки» и вне основной темы статьи мне стало интересно, почему в каком-то месте статьи автор использует следующую идиому:

Листинг 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 или ниже.

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