Я прочитал несколько связанных вопросов, но ни один из них не объясняет способы безопасной публикации Держателя.Я все еще запутался в примере из Java Concurrency in Practice, раздел 3.5:
. Существует класс Holder:
public Holder {
private int n;
public Holder(int n) { this.n = n };
public void assertSanity() {
if(n != n)
throw new AssertionError("This statement is false.");
}
}
и его небезопасная публикация:
//unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
Ошибка AssertionError может быть выдана, и я согласен.Авторы пишут, что это из-за небезопасной публикации, но с другой стороны нет ответа: каков будет правильный способ публикации? Они указывают на 4 безопасных идиомы публикации, но я не понимаю, почемубудут ли они работать в приведенном выше случае:
Чтобы безопасно опубликовать объект, и ссылка на объект, и состояние объекта должны быть видны другим потокам одновременно.Правильно сконструированный объект можно безопасно опубликовать с помощью:
- Инициализация ссылки на объект из статического инициализатора;
- Сохранение ссылки на него в энергозависимом поле или AtomicReference;
- Сохранение ссылки на него в конечном поле правильно построенного объекта;
- или Сохранение ссылки на него в поле, которое надлежащим образом защищено замком.
Я согласен с 1 и 4, но сомневаюсь, почему следующие публикации будут работать:
//safe publication
public volatile Holder holder;
или
//safe publication
public final Holder holder;
volatile & final влияют только на справку, а не надля ссылочного состояния объекта, так что Я думаю, что ошибка AssertionError все еще возможна, верно?
Вместо уточнения публикации, авторы показывают, как сделать Держателя невосприимчивым к небезопасной публикации, путем:
private final int n;
Мне интересно, будет ли работать следующее?Как это связано с (эффективной) неизменностью?
private volatile int n;
Это мой первый вопрос, спасибо за вашу помощь!