Это безопасная публикация объекта?
У меня есть предмет класса
class Item {
public int count;
public Item(int count) {
this.count = count;
}
}
Затем я добавлю ссылку на Item в поле другого класса.
class Holder {
public Item item;
public Holder() {
item = new Item(50);
}
}
Можно ли безопасно опубликовать этот новый объект Item? Если нет, то почему? Согласно Java Concurrency на практике, новый элемент публикуется без полной сборки, но, на мой взгляд, новый элемент полностью построен: его this
ссылка не исчезает, и ссылка на нее и ее состояние публикуется одновременно, поэтому поток потребителя не увидит устаревшее значение. Или это проблема видимости. Я точно не знаю причину.
2 ответа
Можно ли безопасно опубликовать этот новый объект Item? Если нет, то почему?
Проблема связана с оптимизацией и переупорядочением инструкций. Когда у вас есть два потока, которые используют построенный объект без синхронизации, может случиться так, что компилятор решит переупорядочить инструкции для эффективности и выделить пространство памяти для объекта и сохранить его ссылку в item
поле до его завершения с помощью конструктора и инициализации поля. Или он может изменить порядок синхронизации памяти, чтобы другие потоки воспринимали это таким образом.
Если вы отметите item
поле как final
тогда конструктор гарантированно завершит инициализацию этого поля как часть конструктора. В противном случае вам придется синхронизировать блокировку перед ее использованием. Это часть определения языка Java.
Вот еще пара ссылок:
Да, есть проблема видимости, так как Holder.item
не является final
, Таким образом, нет никакой гарантии, что другой поток увидит свое инициализированное значение после построения Holder
закончен.
И, как указал @Gray, JVM может изменять порядок команд при отсутствии барьеров памяти (которые могут быть созданы с помощью блокировки (синхронизации), final
или же volatile
Классификатор). В зависимости от контекста, вы должны использовать один из них здесь для обеспечения безопасной публикации.