'Актуальные' гарантии значений заключительных полей Java расширяются на косвенные ссылки?

Спецификация языка Java определяет семантику заключительных полей в разделе 17.5:

Модель использования для заключительных полей является простой. Установите заключительные поля для объекта в конструкторе того объекта. Не пишите ссылку на объект, создаваемый в месте, где другой поток видит его, прежде чем конструктор объекта будет закончен. Если это будет сопровождаться, то, когда объект замечен другим потоком, тот поток будет всегда видеть правильно созданную версию заключительных полей того объекта. Это будет также видеть версии любого объекта или массива, на который ссылаются те заключительные поля, которые, по крайней мере, так актуальны, как заключительные поля.

Мой вопрос - 'актуальная' гарантия расширяется на содержание вложенных массивов и вложенные объекты?

Вкратце: Если один поток присваивает изменяемый граф объектов заключительному полю в объекте, и граф объектов никогда не обновляется, все потоки могут безопасно считать тот граф объектов через заключительное поле?

Сценарий в качестве примера:

  1. Распараллельте конструкции HashMap ArrayLists, затем присвойте HashMap заключительному полю 'myFinal' в экземпляре класса 'MyClass'
  2. Поток B видит (несинхронизируемую) ссылку на экземпляр MyClass и читает 'myFinal' и доступы и читает содержание одного из ArrayLists

В этом сценарии члены ArrayList, как замечено Потоком B, гарантировал, что был, по крайней мере, так же актуален, как они были когда завершенный конструктор MyClass?

Я ищу разъяснение семантики Модели памяти Java и спецификации языка, а не альтернативных решений как синхронизация. Мой ответ мечты был бы да или не со ссылкой на соответствующий текст.

Обновления:

  • Я интересуюсь семантикой Java 1.5 и выше, т.е. с обновленной Моделью памяти Java, представленной через JSR 133. 'Актуальная' гарантия на заключительных полях была представлена в этом обновлении.

10
задан mattbh 14 May 2010 в 00:29
поделиться

2 ответа

В этом сценарии члены ArrayList, как его видит поток B гарантированно будет как минимум до даты, как они были, когда MyClass конструктор завершен?

Да, есть.

Поток требуется для чтения памяти, когда он впервые встречает ссылку. Поскольку хэш-карта построена, все записи в ней совершенно новые, то ссылки на объекты актуальны по сравнению с тем, чем они были на момент завершения работы конструктора.

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

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

РЕДАКТИРОВАТЬ

Я знал, что где-то раньше видел эту гарантию.

Вот интересный параграф из этой статьи , который описывает безопасность инициализации JSR 133

Новый JMM также стремится предоставить новая гарантия безопасности инициализации - что до тех пор, пока объект правильно сконструирован (это означает, что ссылки на объект нет опубликовано до того, как конструктор завершено), тогда все потоки увидят значения для его последних полей, которые были установлены в его конструкторе, независимо от того, есть ли синхронизация используется для передачи ссылка из одного потока в другой. Кроме того, любые переменные, которые могут быть достигнут через последнее поле правильно построенный объект, такой как поля объекта, на который ссылается final поле, также гарантированно будет также видны другим потокам. Этот означает, что если последнее поле содержит ссылка, скажем, на LinkedList в дополнение к правильному значению ссылка видна другим потоки, а также содержимое этого LinkedList во время строительства будет быть видимым для других тем без синхронизация. В результате значительное укрепление значение final - это последние поля можно безопасно получить доступ без синхронизация, и что компиляторы можно предположить, что поля final не будут изменить и, следовательно, можно оптимизировать множественные выборки.

6
ответ дан 4 December 2019 в 02:49
поделиться

Если конструктор написан так, у вас не должно возникнуть проблем:

public class MyClass {
    public final Map myFinal;
    public MyClass () {
        Map localMap = new HashMap();
        localMap.put("key", new ArrayList());
        this.myFinal = localMap;
    }
}

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

1
ответ дан 4 December 2019 в 02:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: