Последние поля и потокобезопасность
Должны ли это быть все поля, включая суперполя, целенаправленно неизменяемого Java-класса 'final', чтобы быть потокобезопасным, или этого достаточно, чтобы не иметь методов-модификаторов?
Предположим, у меня есть POJO с неконечными полями, где все поля являются типом некоторого неизменяемого класса. Этот POJO имеет геттеры-сеттеры и конструктор, который устанавливает некоторое начальное значение. Если я расширю этот POJO с помощью методов-модификаторов, выбив их, таким образом, сделав его неизменным, будет ли класс расширения поточно-ориентированным?
2 ответа
Для того, чтобы использовать эффективно неизменный объект без final
Поля потокобезопасным образом, вам нужно использовать одну из безопасных идиом публикации, когда объект становится доступным для других потоков после инициализации, в противном случае эти потоки могут видеть объект в частично инициализированном состоянии (из Java Concurrency in Practice):
- Инициализация ссылки на объект из статического инициализатора;
- Сохранение ссылки на него в энергозависимом поле или AtomicReference;
- Сохранение ссылки на него в конечном поле правильно построенного объекта; или же
- Сохранение ссылки на него в поле, которое должным образом охраняется блокировкой.
Объявление полей вашего неизменяемого объекта как final
снимает это ограничение (т.е. он гарантирует, что если другие потоки увидят ссылку на объект, они также увидят его final
поля в полностью инициализированном состоянии). Однако в общем случае это не гарантирует, что другие потоки смогут увидеть ссылку на объект, как только он был опубликован, поэтому вам, возможно, все же придется использовать безопасную публикацию, чтобы обеспечить его.
Обратите внимание, что если ваш объект реализует интерфейс, вы можете использовать подход, используемый Collections.unmodifiableList()
, так далее:
class ImmutableFooWrapper implements IFoo {
private final IFoo delegate; // final provides safe publication automatically
public ImmutableFooWrapper(IFoo delegate) {
this.delegate = delegate;
}
...
}
public IFoo immutableFoo(IFoo foo) {
return new ImmutableFooWrapper(foo);
}
Да, он будет неизменным и, следовательно, потокобезопасным, но только до тех пор, пока поля являются частными.