final vs volatile guaranntee w.rt для безопасной публикации объектов

Из книги Java concurrency на практике:

Чтобы безопасно опубликовать объект, и ссылка на объект, и его состояние должны быть видны другим потокам одновременно. Правильно сконструированный объект можно безопасно опубликовать:

  • Инициализация ссылки на объект из статического инициализатора

  • Сохранение ссылки на него в volatile поле или AtomicReference

  • Сохранение ссылки на него в конечном поле правильно построенного объекта

  • Сохранение ссылки на него в поле, которое должным образом защищено блокировкой
    .

Мои вопросы:

  1. В чем разница между пунктами 2 и 3? Меня интересует разница между подходом volatile и подходом final с точки зрения безопасной публикации объекта.
  2. Что он имеет в виду под конечным полем правильно сконструированного объекта в пункте 3? Перед тем, как начать маркированные пункты, авторы уже упомянули, что они говорят о правильно сконструированном объекте (который, как я предполагаю, не позволяет ссылке this избежать ). Но опять же, почему они упомянули о правильно построенных объектах?

person Geek    schedule 09.02.2013    source источник


Ответы (2)


В чем разница между пунктами 2 и 3?

  • volatile в основном означает, что любые записи в это поле будут видны из других потоков. Поэтому, когда вы объявляете поле как volatile: private volatile SomeType field;, вам гарантируется, что если конструктор запишет в это поле: field = new SomeType();, это назначение будет видно другим потокам, которые впоследствии попытаются прочитать field.
  • final имеет очень похожую семантику: у вас есть гарантия, что если у вас есть конечное поле: private final SomeType field; запись в это поле (либо в объявлении, либо в конструкторе): field = new SomeType(); не будет переупорядочена и будет видна другим потокам если объект правильно опубликован (например, нет экранирования this).

Очевидно, основное отличие состоит в том, что если поле является окончательным, вы можете назначить его только один раз.

Что он имеет в виду под конечным полем правильно построенного объекта в точке 3?

Если, например, вы позволите this выйти из конструктора, гарантия, предоставляемая окончательной семантикой, исчезнет: наблюдающий поток может увидеть поле со значением по умолчанию (null для объекта). Если объект правильно построен, этого не может произойти.


Надуманный пример:

class SomeClass{
    private final SomeType field;

    SomeClass() {
        new Thread(new Runnable() {
            public void run() {
                SomeType copy = field; //copy could be null
                copy.doSomething(); //could throw NullPointerException
            }
        }).start();
        field = new SomeType();
    }
}
person assylias    schedule 09.02.2013
comment
Если после построения я изменю состояние объекта, на который ссылается поле, например field.setX(new X()), гарантируется ли, что это изменение также будет замечено другими потоками, когда мы объявим его как volatile? Или это только обеспечивает безопасность инициализации? Как насчет финала в этом случае? - person Geek; 10.02.2013
comment
Нет, только field гарантирует видимость при (повторном) назначении. Если x также не является изменчивым, field.x = new X(); (или ваш пример установки) не дает такой гарантии. - person assylias; 10.02.2013
comment
То же самое относится и к final, из чего следует, что если все члены field также являются окончательными и неизменяемыми, то field является неизменяемым и, следовательно, потокобезопасным. - person assylias; 10.02.2013
comment
@assylias Я несколько раз читал ваш ответ, но так и не понял. Поскольку поле является окончательным, никакого переупорядочения не будет, почему новый поток не увидит атрибут поля этого? Вы говорите, это потому, что конструктор еще не завершен? - person Abidi; 09.09.2013
comment
У вас даже больше гарантий при использовании полей final: [другой поток] также увидит версии любого объекта или массива, на которые ссылаются эти окончательные поля, которые, по крайней мере, так же актуальны, как и окончательные поля. (источник) - person Marcono1234; 03.05.2019

Нет никакой разницы в эффектах публикации volatile и final, за исключением того, что final можно установить только один раз в конструкторе, и поэтому то, что вы читаете, никогда не должно меняться.

Я считаю, что правильно сконструированный объект действительно является тем, на что вы ссылаетесь, объектом, чья ссылка this не ускользнула от его конструктора и была опубликована безопасным образом в потоке, в котором он используется.

person Steven Schlansker    schedule 09.02.2013