Во-первых, ваш класс (как написано) является фактически неизменным (с точки зрения API), потому что нет операции, которая позволяет что-то изменять список после его создания.
(Вы потенциально могут выполнять мутирует операции на ElementClass
экземпляров в списке. Но это не мутирует сам список.)
Если вы сделали добавить операцию мутирует к ListContainer
, вам нужно будет сделать что-то, чтобы сделать их потокобезопасными. Но если предположить, что внутренний объект List
не «течет», было бы достаточно сделать соответствующие операции ListContainer
синхронизированными.
На самом деле класс, написанный не на 100% поточно-безопасный. Один поток может создать экземпляр, передать ссылку на другой поток, а для второго потока - просмотреть несогласованную версию состояния списка экземпляра, когда он вызывает метод getElement
. Это происходит потому, что нет происходит до или порядок синхронизации связь между завершением конструктора и последующими вызовами метода. См. JLS 17.4.4 и 17.4.5.
Это может быть «исправлено» несколькими способами. Например:
- объявить
getElement
как метод synchronized
,
- объявить
elements
, как volatile
или
- объявить
elements
, как final
.
Последний работает из-за специальных правил для окончательных полей; см. JLS 17.5. (Если ListContainer
предназначено быть неизменным, объявляя elements
, как final
является лучшим решением ...)
Как я могу убедиться, что та же ссылка на ElementClass
объект приобретается не на два разные потоки и рискнуть неожиданным поведением?
Это было бы возможно, но для этого потребовалась бы дополнительная структура данных ... что треки, которые ElementClass
экземпляров были «выписаны». И у вас возникнет проблема, когда клиенты не «возвращают» экземпляры, реализуют очереди и т. Д.
Это можно сделать, изменив класс ListContainer
...
Только с большим трудом; см. выше.
... или все меры безопасности нити должны быть приняты в классе ElementClass
?
Это самый простой и лучший подход. Либо сделайте соответствующие (индивидуальные) операции синхронизированными, либо разработайте «протокол» для блокировки, если вам требуется взаимное исключение для последовательностей операций. («Протокол», вероятно, всего лишь «правило» для того, какой замок использовать ...)
Если вы мутируете объект 'ElementClass' с помощью некоторого метода' ElementClass', вы должны «синхронизировать» этот метод мутации. Это гарантирует, что в то время только один поток сможет мутировать этот объект с помощью этого метода. –