Распространяются ли «актуальные» гарантии на значения окончательных полей Java на косвенные ссылки? - PullRequest
10 голосов
/ 14 мая 2010

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

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

У меня вопрос: распространяется ли «актуальная» гарантия на содержимое вложенных массивов и вложенных объектов?

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

Пример сценария:

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

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

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

Обновления:

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

Ответы [ 2 ]

6 голосов
/ 14 мая 2010

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

Да, они есть.

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

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

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

EDIT

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

Вот интересующий вас абзац из этой статьи , которая описывает JSR 133

Инициализация безопасности

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

1 голос
/ 14 мая 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...