Должен ли я использовать как lock, так и volatile?

#java #locking #volatile

#java #блокировка #volatile

Вопрос:

Мое понимание volatile заключается в том, что оно гарантирует, что значение всегда считывается из памяти, поэтому, насколько я вижу, в следующем примере myObject переменная должна быть volatile, чтобы избежать NullPointerException возникновения:

 private final Object lock = new Object();
private MyObject myObject = null;

//...

synchronized (lock) {
    if (myObject == null) {
        myObject = new MyObject();
    }

    myObject.blah();

    // some other stuff that I want synchronized
}
  

myObject только когда-либо затрагивается в синхронизированном блоке. lock используется только каждый для синхронизации этого блока.

Это правильно?

Итак, слегка перефразировав, мой вопрос … представьте, что два потока попадают в этот код. Первый поток блокирует и устанавливает MyObject, вызовы .blah() и любой другой код в синхронизированном блоке и выходит из синхронизированного блока. Это позволяет второму потоку входить в синхронизированный блок. myObject Есть ли вероятность, что он все еще может оценить значение volatile без установки значения volatile myObject == null true ?

Комментарии:

1.Является myObject volatile? Кроме того, является Object lock постоянным для всех экземпляров вашего класса?

2. @LuiggiMendoza — см. Редактирование.

3. Есть ли какой-либо другой код, который можно установить myObject на null ?

4. @SotiriosDelimanolis — нет, представьте, что этот код является автономным.

5. Я думаю, что может быть шанс, у меня был не очень удачный опыт использования synchronized блоков, когда возникали неожиданные результаты и синхронизация не работала, особенно в начале процесса. Лучше использовать ReentrantLock . А что касается volatile , в данном случае это не имеет ничего общего с синхронизацией, поэтому не требуется.

Ответ №1:

synchronized Блок гарантирует, что обновления памяти будут видны другим потокам. Нет необходимости создавать myObject volatile.

Из встроенных блокировок и синхронизации:

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

Комментарии:

1. Я провел некоторое исследование, так как мне было интересно, почему я думал, что вам нужно ключевое слово volatile. Я полагаю, что это было связано с DCL, это: cs.umd.edu /~pugh/java/memoryModel/DoubleCheckedLocking.html . Теперь я прав, говоря, что, поскольку у меня нет проверки на null за пределами моего цикла синхронизации, мне не нужна проверка?

Ответ №2:

Я думаю, что volatile здесь не нужен, потому что каждый поток, который переходит в синхронизированный блок, проверяет ссылки на MyObject, поэтому MyObject должен быть создан, когда первый поток переходит в блок, другие потоки защищены проверкой, является ли MyObject not null . Для меня выглядит хорошо.

РЕДАКТИРОВАТЬ: я надеюсь, что есть только один блок, в котором вы хотите использовать ссылку myObect, и вы не изменяете эту ссылку до или после блока синхронизации.

Комментарии:

1. Но нет ли вероятности, что значение может находиться где-то в кэше и, следовательно, приводить к NPE?

2. Нет. Такой возможности нет.

3. В коде, который вы вставляете, каждый поток (но только один одновременно) будет проверять значение MyObject, затем запускать метод blah, поэтому все остальные потоки будут ждать. Но вы не можете реализовать второй блок, в котором вы устанавливаете MyObject=null , потому что тогда произойдет что-то вроде того, что вы сказали. Если вы хотите установить значение null для MyObject, вам нужно сделать это в одном блоке.