Многие вопросы/ответы указали, что если объект класса имеет поле 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
?