Безопасная инициализация нулевой ссылки

Мне интересно, какие гарантии публикации существуют для не конечного поля, инициализированного нулевым, если оно есть.

Рассмотрим следующий фрагмент:

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 увидит что-то ненулевое?

Нет.

Другие вопросы по тегам