Скажите, что у меня есть два потока и объект. Один поток присваивает объект:
public void assign(MyObject o) {
myObject = o;
}
Другой поток использует объект:
public void use() {
myObject.use();
}
Переменная myObject должны быть объявлены как энергозависимые? Я пытаюсь понять, когда использовать энергозависимый и если не, и это является озадачивающим меня. Действительно ли возможно, что второй поток сохраняет ссылку на старый объект в его кэше локальной памяти? В противном случае, почему нет?
Большое спасибо.
В этом случае вы можете использовать volatile. Вам потребуется volatile, синхронизация доступа к переменной или какой-либо аналогичный механизм (например, AtomicReference), чтобы гарантировать, что изменения, внесенные в поток назначения, действительно видны для потока чтения.
Я пытаюсь понять, когда использовать volatile, а когда нет
Вам следует избегать его использования. Вместо этого используйте AtomicReference (или другой атомарный класс, если необходимо). Эффекты памяти такие же, и цель намного яснее.
Я настоятельно рекомендую прочитать превосходный Java Concurrency in Practice для лучшего понимания.
Здесь есть несколько сбивающих с толку комментариев: чтобы уточнить, ваш код неверен в его нынешнем виде, предполагая, что два разных потока вызывают assign ()
и используйте ()
.
При отсутствии volatile
или другой связи «происходит раньше» (например, синхронизация по общей блокировке) любая запись в myObject
в assign ()
не гарантируется, что его увидит поток, вызывающий use ()
- ни сразу, ни вовремя, ни когда-либо.
Да, volatile
- это один из способов исправить это (при условии, что это некорректное поведение - есть вероятные ситуации, когда вас это не волнует!).
Вы совершенно правы в том, что поток 'use' может видеть любое 'закешированное' значение myObject
, включая то, которое ему было присвоено во время построения, и любое промежуточное значение (опять же, при отсутствии другого -перед точками).
Оставив позади сложные технические детали, вы можете увидеть volatile
меньше или больше как синхронизированный
модификатор для переменных . Если вы хотите синхронизировать доступ к методам или блокам, вы обычно хотите использовать модификатор synchronized
следующим образом:
public synchronized void doSomething() {}
Если вы хотите «синхронизировать» доступ к переменным, тогда вы хотели бы использовать модификатор volatile
:
private volatile SomeObject variable;
За кулисами они делают разные вещи, но эффект тот же: изменения сразу видны для следующего потока доступа .
В вашем конкретном случае я не думаю, что модификатор volatile
имеет какое-либо значение. volatile
никоим образом не гарантирует, что поток, назначающий объект, будет запускаться перед потоком, использующим объект.Может быть и наоборот. Вероятно, вы просто хотите сначала выполнить нулевую проверку в методе use ()
.
Обновление : также см. эту статью :
Доступ к переменной действует так, как если бы заключен в синхронизированный блок, синхронизированный сам с собой. Мы говорим «действует так, как если бы» во втором пункте, потому что для программиста по крайней мере (и, вероятно, в большинстве реализаций JVM) нет фактического объекта блокировки .