RestTemplate线程安全吗?
Spring RestTemplate
是否是线程安全的? 那是
-
RestTemplate
是多个连接可以安全共享的策略对象。 要么 -
RestTemplate
是一个连接对象(如数据库连接),在使用时不能共享,并且需要为每个连接重新创build或合并。
RestTemplate
是线程安全的 (强调添加):
从概念上讲,它与
JdbcTemplate
,JmsTemplate
以及Spring Framework和其他项目组合项目中的其他各种模板非常相似。 这意味着,例如, 一旦构buildRestTemplate
就是线程安全的
如果您检查RestTemplate
的源代码,您将会看到,在构造对象之后,它不使用synchronized
方法或volatile
字段来提供线程安全性。 所以在构build之后修改RestTemplate
对象是不安全的。
特别是添加消息转换器是不安全的。 要提供消息转换器列表,您必须执行以下操作之一:
- 使用
RestTemplate(List<HttpMessageConverter<?>> messageConverters)
构造函数。 由于messageConverters
的内部列表是final
,所以这将安全地发布消息转换器的列表 。 - 使用
setMessageConverters(List<HttpMessageConverter<?>> messageConverters)
mutator ,然后安全地发布已更改的RestTemplate
对象。 使用具有<property name="messageConverters"><list>...
的Spring bean定义<property name="messageConverters"><list>...
这样做,因为在大多数实际使用情况下, 将通过设置容器的线程安全地发布 bean。 - 对
getMessageConverters()
返回的引用使用List.add
,然后安全地发布已更改的RestTemplate
对象。 但是,RestTemplate
的文档没有明确说明它返回了一个可以用来改变消息转换器列表的引用。 当前的实现,但可能的实现可能会改变返回一个Collections.unmodifiableList
或列表的副本。 所以最好不要这样改变它
从图书馆的angular度来看,这是线程安全的。 例如,getMessageConverters()是公共的,这意味着如果有人得到了列表并将其修改超出了库的目的,那么它会引起问题(甚至是setter方法,如果在RestTemplate实例化之后随时调用的话 – 当被其他线程显然使用,繁荣!)。 这可能是Ross发生了什么(没有足够的声望来回答答案,但是我支持线程安全的而不是线程安全的参数)
好吧,尽pipe我可能会从源代码pipe理中挖掘出造成这些问题的旧代码。
我认为,即使在创build时同步也存在另一个线程可以修改内部集合的情况,这是公平的。 所以最好要小心。 看着旧的代码,是的,它实际上是使用消息转换器。 但只有在创build时同步。
restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
之后,与RestTemplate的唯一交互是这样的:
return restTemplate.postForObject(url, object, clazz);
这也是最终抛出exception的线。
当然,与消息转换器没有交互(我们没有本地引用)。
看着stacktrace和spring的源代码,错误发生在这一行:
for (HttpMessageConverter<?> converter : getMessageConverters()) {
那么我们有什么?
- 我们可以同时访问messageConverters
- 如果我们的代码没有这样做,那么哪个代码呢? 我没有答案。 我当时的解决scheme是每次创build一个新的RestTemplate,因为性能不是这个应用程序的关注点。
所以总而言之,在某些情况下,事情可能不是线程安全的,当然如果你直接使用消息转换器。 这种情况虽然是一个奇怪的,但我认为这将是有益的发布它。
讨厌不同意上面的答案(强调增加),但不是不是线程安全的。 即使在创build之后。 在内部,它正在玩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)