0

Я не мог понять ниже фрагмент кода от A fix that doesn't work. (Я прочитал объяснение, которое следует на той же странице).DCL с двумя синхронизированными блоками сломан?

Если у нас есть 2 синхронизированных блока, как эта версия DCL сломана? Или это не применимо после Java5?

// (Still) Broken multithreaded version 
// "Double-Checked Locking" idiom 
class Foo { 
    private Helper helper = null; 
    public Helper getHelper() { 
    if (helper == null) { 
     Helper h; 
     synchronized(this) { 
     h = helper; 
     if (h == null) 
      synchronized (this) { 
       h = new Helper(); 
      } // release inner synchronization lock 
     helper = h; 
     } 
     }  
    return helper; 
    } 
    // other functions and members... 
    } 
+0

Внутренний «синхронизированный» блок абсолютно бессмыслен. Нить не делает _not_ приобретает блокировку, когда она входит во внутренний блок, потому что она уже владеет ею, и она не освобождает блокировку, когда она покидает внутренний блок, потому что она должна продолжать владеть им, пока не покинет внешний блок. –

ответ

3

Там нет никакой гарантии, что поток, который видит helper не нуль будет в состоянии видеть все операции записи, сделанные new Helper();. Таким образом, вы можете получить доступ к коррумпированной версии синглтона. Вам нужно что-то в нитке, которая видит helper как не null, чтобы гарантировать, что он видит, что послеh = new Helper(); завершает свою работу. Наблюдение за изменением к энергонезависимой переменной не устанавливает такие отношения, и это все, что делает поток.

Облегчение упрощения, так как модель видимости памяти Java - это то, что два потока выполняют что-то, что устанавливает связь «происходит до»/«происходит после» между двумя операциями, выполняемыми двумя потоками. Это может включать операции внутри синхронизированного блока или доступ к изменчивой переменной.

Но с вашим кодом выше, нить может заметить, что helper не null, а затем перейти к объекту, созданному new Helper(). Он не должен иметь доступ к переменной volatile и не должен вводить синхронизированный блок. Таким образом, нет ничего, что могло бы установить требуемое отношение «произойдет после», чтобы убедиться, что оно видит какие-либо изменения, сделанные new Helper().

+0

Ах, поэтому он похож на обычный шаблон DCL, мы не избегаем доступа к неполному конструированному объекту. Таким образом, второй синхронизированный блок не служит никакой цели. Благодаря! – ADJ

+1

Правильно. Хотя на практике это может изменить его от «иногда случается, чтобы не срабатывать», чтобы «работать надежно на некоторых системах, в которых я его пробовал». –

+0

Синхронизированный блок не может использоваться по двум причинам: 1) Вещи, которые происходят после выхода синхронизированного блока, никак не синхронизированы, а запись в «помощник» после выхода. 2) Даже если это гарантировало, что записи произошли по порядку, это не гарантирует, что они будут соблюдаться по порядку, потому что энергонезависимые, несинхронизированные чтения могут быть переупорядочены. –