Многие вопросы/ответы указали, что если объект класса имеет поле final, и никакая ссылка на него не подвергается ни одному другому потоку во время построения, тогда все потоки гарантированно будут видеть значение, записанное в поле, когда конструктор завершается. Они также указали, что сохранение в поле final ссылки на изменяемый объект, к которому никогда не обращались внешние потоки, гарантирует, что все мутации, которые были сделаны объекту перед хранилищем, будут видны во всех потоках, объект через поле. К сожалению, ни одна из гарантий не распространяется на записи полей не final.
Однако вопрос, который я не вижу, заключается в следующем: если семантика класса такова, что поле не может быть final, но мы хотим обеспечить "публикацию" поля и объекта, идентифицированного таким образом, каков наиболее эффективный способ сделать это? В качестве примера рассмотрим
class ShareableDataHolder<T>
{
Object data; // Always identifies either a T or a SharedDataHolder<T>
}
private class SharedDataHolder<T> extends ShareableDataHolder<T>
{
Object data; // Always identifies either a T or a lower-numbered SharedDataHolder<T>
final long seq; // Immutable; necessarily unique
}
Предполагалось, что data будет сначала идентифицировать объект данных напрямую, но он может быть законным в любое время изменен для идентификации SharedDataHolder<T>, который прямо или косвенно инкапсулирует эквивалентный объект данных. Предположим, что весь код написан правильно (хотя и не обязательно оптимально-эффективно), если любое чтение data может произвольно возвращать любое значение, которое когда-либо было написано на data, но может выйти из строя, если оно читает null.
Объявление volatile Object data будет семантически правильным, но, скорее всего, наложит дополнительные затраты на каждый последующий доступ к полю. Ввод фиктивного замка после первоначальной установки поля будет работать, но будет бесполезно медленным. Наличие фиктивного поля final, которое объект устанавливает для идентификации, похоже, что он должен работать; хотя технически я думаю, что это может потребовать, чтобы все обращения к другому полю были сделаны через другое поле, я не вижу реалистичного сценария, где это имеет значение. В любом случае наличие фиктивного поля, целью которого является только обеспечить соответствующую синхронизацию через его существование, представляется расточительным.
Есть ли какой-либо чистый способ сообщить компилятору, что конкретная запись в data внутри конструктора должна иметь отношение "до-до" относительно любых чтений этого поля, которые возникают после возвращения конструктора (как это было бы в случае если поле было final), без необходимости оплачивать расходы, связанные с volatile, блокировки и т.д.? В качестве альтернативы, если поток должен был читать data и найти его null, может ли он каким-то образом повторить чтение таким образом, чтобы установить "произойдет после" в отношении записи data [признавая, что такой запрос может быть медленным, но не обязательно, чтобы это происходило очень часто]?
PS - Если происходит - до того, как отношения являются нетранзитивными, произойдет ли правильное действие - до того, как отношения будут существовать в следующем сценарии?
- Thread 1 записывает в не конечное поле
datв некоторый объектFredи сохраняет ссылку на него в конечное полеGeorge. - Резьба 2 копирует ссылку из
Georgeв поле без полейLarry. - Тема 3 читает
Larry.dat.
Из того, что я могу сказать, между записью поля Fred dat и чтением George существует связь между случаем и прошлым. Было бы существовать до отношения между записью Fred dat и чтением Larry, которая возвращает ссылку на Fred, которая была скопирована из ссылки final на Fred? Если нет, существует ли какой-либо "безопасный" способ скопировать ссылку, содержащуюся в поле final, в поле не окончательного значения, которое будет доступно через другие потоки?
PPS. Если объект и его составляющие никогда не обращаются за пределами своей создаваемой нити до тех пор, пока главный конструктор не закончит, а последний шаг главного конструктора будет состоять из хранилищ в главном объекте a final ссылка на себя, есть ли любая "правдоподобная" реализация/сценарий, когда другой поток мог видеть частично построенный объект, независимо от того, действительно ли что-то использует эту ссылку final?