Подтвердить что ты не робот

Безопасен ли поток RestTemplate?

Является ли Spring RestTemplate потокобезопасным? Это

  • Является RestTemplate объектом стратегии, с которым можно безопасно делиться несколькими соединениями. или
  • Является RestTemplate объектом соединения (например, соединение с базой данных), которое не может использоваться совместно во время использования и требует создания заново или объединения для каждого соединения.
4b9b3361

Ответ 1

RestTemplate является потокобезопасным (выделено):

Концептуально он очень похож на JdbcTemplate, JmsTemplate и различные другие шаблоны, найденные в Spring Framework и других портфельных проектах. Это означает, например, что RestTemplate является поточно-ориентированным после его создания


Объекты класса RestTemplate не изменяют какую-либо информацию о своем состоянии для обработки HTTP: класс является экземпляром шаблона проектирования Стратегии, а не похож на объект подключения. Без информации о состоянии, нет никакой возможности повредить различные потоки или информацию о состоянии гонки, если они совместно используют объект RestTemplate. Вот почему потоки могут совместно использовать эти объекты.

Если вы изучите исходный код RestTemplate, то увидите, что он не использует методы synchronized или поля volatile для обеспечения безопасности потока после создания объекта. Поэтому небезопасно изменять объект RestTemplate после создания. В частности, добавлять конвертер сообщений небезопасно.

Чтобы предоставить ему список конвертеров сообщений, вы должны выполнить одно из следующих действий:

  • Используйте конструктор RestTemplate(List<HttpMessageConverter<?>> messageConverters). Поскольку внутренним списком messageConverters является final, он безопасно публикует список конвертеров сообщений.
  • Используйте мутатор setMessageConverters(List<HttpMessageConverter<?>> messageConverters) и, а затем безопасно опубликуйте измененный объект RestTemplate. Для этого используется определение bean-компонента Spring с <property name="messageConverters"><list>..., поскольку bea будет безопасно опубликован потоком, устанавливающим контейнер, в большинстве случаев практического использования.
  • Используйте List.add для ссылки, возвращенной getMessageConverters(), а затем безопасно публикуйте измененный объект RestTemplate. Однако в документации для RestTemplate прямо не указывается, что она возвращает ссылку, которую можно использовать для изменения списка конвертеров сообщений. Текущая реализация делает, но, возможно, реализация может быть изменена, чтобы вернуть Collections.unmodifiableList или копию списка. Так что может быть лучше не менять это таким образом.

Обратите внимание, что первый случай - это единственный способ настроить конвертеры сообщений при создании объекта, поэтому is правильно сказать, что он "является поточно-ориентированным после его создания".

Класс является частью Spring Framework, поэтому практически во всех практических случаях объекты класса будут устанавливаться как часть контекста приложения Spring с использованием первого (внедрение зависимостей с помощью конструктора) или второго (внедрение зависимостей с использованием установщика) методы и так будут гарантированно безопасно опубликованы в нескольких потоках.

Ответ 2

Это потокобезопасность с точки зрения библиотеки. Например, getMessageConverters() является общедоступным. Это означает, что если кто-то задержится в списке и изменяет его вне цели библиотеки, тогда он вызовет проблемы (и даже метод setter, если он вызовет в любой момент после создания RestTemplate - и, хотя они используются другими потоками, очевидно, стрела!). Скорее всего, это случилось с Росс (недостаточно репутации для ответа на ответ, но я поддерживаю как поточно-безопасные, так и небезопасные аргументы)

Ответ 3

Хорошо, хотя я мог бы выкопать старый код из исходного элемента управления, который вызвал эти проблемы.

Я думаю, было бы справедливо сказать, что даже синхронизация при создании существует, когда другой поток может изменять внутренние коллекции. Так что лучше быть осторожным. Глядя на старый код, да, на самом деле он использовал конвертер сообщений. Но только при синхронизации при создании.

restTemplate = new RestTemplate();

restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

После этого единственным взаимодействием с RestTemplate было следующее:

return restTemplate.postForObject(url, object, clazz);

Это также строка, которая в итоге генерирует исключение.

Конечно, нет никакого взаимодействия с конвертером сообщений (у нас нет локальной ссылки на него).

Посмотрев на stacktrace и исходный код spring, произошла ошибка в этой строке:

for (HttpMessageConverter<?> converter : getMessageConverters()) {

Итак, что у нас есть?

  • У нас есть одновременный доступ к messageConverters
  • Если наш код этого не сделал, то какой код сделал? У меня нет ответа. Мое решение в то время состояло в том, чтобы создавать новый RestTemplate каждый раз, поскольку производительность в этом приложении не была проблемой.

Итак, есть обстоятельства, когда вещи не могут быть потокобезопасными, конечно, если бы вы играли с конвертерами сообщений напрямую. Этот случай, однако, странный, но я подумал, что было бы полезно его опубликовать.

Ответ 4

Ненавидьте, чтобы не согласиться с принятым ответом выше (выделено мной), но нет, это не Thread safe. Даже после творения. Внутри он играет с ArrayLists, я не врывался в источник. Я видел слишком много таких:

java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback.doWithRequest(RestTemplate.java:677)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:567)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:545)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:253)