Безопасная инициализация нулевой ссылки
Мне интересно, какие гарантии публикации существуют для не конечного поля, инициализированного нулевым, если оно есть.
Рассмотрим следующий фрагмент:
public class MyClass {
private final CopyOnWriteArrayList<Inner> list = new CopyOnWriteArrayList<>();
//called by thread 1
public void init() {
// initialise Inner instance
list.add(new Inner());
}
//called by thread 2
public void doSomething() {
for (Inner i : list) {
// access non-final field
Object ref = i.ref;
// do something
// ...
// ...
// potentially set i.ref
}
}
private static class Inner {
// initialised by thread 1
Object ref = null;
}
}
Если предположить, doSomething()
всегда вызывается потоком 2, это безопасно? Какие гарантии дает то, что поток 2 увидит при первом обращении к нему? Есть ли вероятность того, что поток 2 увидит что-то ненулевое?
Где в JMM описана семантика вокруг этой ситуации?
2 ответа
JVM
будет гарантировать, что вы не видите ничего из воздуха, так что ничего, кроме null
это невозможно, если List
не пусто (в этом примере, конечно). Если бы был вовлечен другой поток (скажем, Thread3
) что бы изменить ваш список (добавить элементы к нему), Thread2
мог видеть эти обновления. Просто обратите внимание, что отдельные методы CopyOnWriteArrayList
потокобезопасны; ваш метод doSomething
нет
Смотрите JLS для уточнения или отличной (и довольно сложной, может быть, только для меня) статьи Алексея.
Разговор о безопасной публикации имеет смысл только тогда, когда вы инициализируете поле значимым объектом, имеющим состояние. Тогда неправильная публикация может привести к наблюдению частично построенного объекта.
В этом случае, null
не передает никакого состояния. Это можно считать неизменным объектом. Неизменяемые объекты не имеют проблем с публикацией.
Какие гарантии дает то, что поток 2 увидит при первом обращении к нему?
Нить 2 увидим null
при обращении к i.ref
,
Обратите внимание, что список может быть пустым, потому что поток 1, возможно, не добавил Inner
к этому еще.
Есть ли вероятность того, что поток 2 увидит что-то ненулевое?
Нет.