Как скоупы Spring Bean влияют на потокобезопасность?
Что проверяют
Понимаете ли вы, что скоуп определяет, сколько экземпляров бина создаст контейнер и на какой срок, но сам по себе потокобезопасности не даёт. Следом почти всегда спрашивают, что будет, если внедрить бин со скоупом prototype или request в singleton.
Как отвечать
Пройдитесь по скоупам с точки зрения общего состояния:
- singleton (по умолчанию): один экземпляр на контейнер, его методы вызывают все потоки сразу. Безопасен, только если не хранит изменяемого состояния: зависимости
finalчерез конструктор, данные запроса в параметрах метода; - prototype: новый экземпляр на каждое получение из контейнера, а не на поток. Внедрённый в singleton, он создаётся один раз и дальше делит судьбу синглтона. Нужен свежий экземпляр, берите
ObjectProvider<T>или@Lookup. Методы уничтожения для prototype Spring не вызывает; - request: экземпляр на HTTP-запрос. Обычно запрос обслуживает один поток, но при
@AsyncилиCompletableFutureработа уходит в другой поток, где контекста запроса уже нет; - session: экземпляр на сессию, и он не потокобезопасен. Две вкладки или параллельные AJAX-запросы одного пользователя работают с ним одновременно;
- application и websocket: общий экземпляр на
ServletContextи на сессию WebSocket соответственно.
Чтобы внедрить request или session в singleton, нужен прокси: @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) или готовая @RequestScope, которая делает то же самое.
Если состояние в синглтоне всё же нужно: ConcurrentHashMap, AtomicLong, неизменяемые объекты, в крайнем случае узкая синхронизация. ThreadLocal в пуле потоков очищают через remove() в finally.
Где спотыкаются
Говорят, что prototype решает проблему многопоточности. Он не привязан к потоку, и один экземпляр вполне может использоваться несколькими потоками.
Считают session-бин безопасным, раз он «у каждого пользователя свой». Параллельные запросы из одной сессии легко ломают его поля.
Вешают synchronized на все методы сервиса. Потокобезопасно, но под нагрузкой все запросы выстраиваются в очередь на одном мониторе.