RestTemplate线程安全吗?


69

Spring是RestTemplate线程安全的吗?那是

  • RestTemplate可以安全共享多个连接的策略对象。要么
  • 是一个RestTemplate连接对象(如数据库连接),使用时无法共享,并且需要为每个连接重新创建或池化。

3
我正在鼓励自己回答自己的问题。
Raedwald 2014年



Answers:


96

RestTemplate 是线程安全的(添加了重点):

从概念上讲,它是非常相似的JdbcTemplateJmsTemplate和Spring框架和其他投资项目中发现的各种其他模板。举例来说,这意味着RestTemplate一旦构建后,线程安全


RestTemplate该类的对象不会更改其任何状态信息来处理HTTP:该类是Strategy设计模式的一个实例,而不是像一个连接对象。没有状态信息,如果它们共享一个RestTemplate对象,就不可能有不同的线程破坏或加速状态信息。这就是为什么线程可以共享这些对象的原因。

如果检查源代码,RestTemplate您将发现在构造对象之后,它不使用synchronized方法或volatile字段来提供线程安全。因此,它是不是安全的修改RestTemplate施工后的对象。特别是,添加消息转换器是不安全的。

要为它提供消息转换器列表,您必须执行以下操作之一:

  • 使用RestTemplate(List<HttpMessageConverter<?>> messageConverters)构造函数。作为messageConvertersis的内部列表final,它安全地发布了消息转换器的列表
  • 使用setMessageConverters(List<HttpMessageConverter<?>> messageConverters)增变器然后安全地发布更改的RestTemplate对象。使用具有此功能的Spring bean定义<property name="messageConverters"><list>...,因为在大多数实际使用情况下,将通过设置容器的线程安全地发布bean 。
  • List.add在返回的引用上使用getMessageConverters(),然后安全地发布更改的RestTemplate对象。但是,for的文档RestTemplate未明确声明其返回了可用于更改消息转换器列表的引用。当前实现可以,但是可能实现可以更改为返回Collections.unmodifiableList列表的a或副本。因此,最好不要以这种方式进行更改。

需要注意的是第一种情况是构建对象时建立的消息转换器的唯一手段,因此它正确的说,它“是线程安全的,一旦构成”。

该类是Spring框架的一部分,因此在几乎所有实际情况下,该类的对象都将被设置为Spring Application Context的一部分,使用第一个(使用构造函数的依赖注入)或第二个(使用setter的依赖注入)方法,因此可以保证安全地发布到多个线程。


6
它不仅是线程安全的,而且其创建似乎很昂贵。我最近在tomcat上遇到了主要的性能问题,因为RestTemplate的初始化导致了类加载器锁争用。
鲍里斯·特鲁霍霍夫

3
我认为它的线程不安全。它使用messageconverters,您可以在运行时从两个不同的线程添加messageconverters。并且它会在执行restTemplate.exchange()调用时引发并发修改异常,因为它会迭代此messageconverters。
樱花2015年

1
@BorisTreukhov我也注意到调试时性能下降,所以我在Google上搜索并找到了它。
Alfredo Osorio

2
@comiventor如果您不使用自定义messageConverters,则可以使用RestTemplate的一个实例。否则,通过构造函数注入messageConverters-也是线程安全的。
user1697575

2
“一旦构建线程安全”似乎意味着“只要您不更改其状态就保证线程安全”,这是正确的,因为只要对象的状态不变,任何对象都是线程安全的。
Mzzl

2

从库的角度来看,它是线程安全的。例如,getMessageConverters()是公共的,这意味着如果有人抓住列表并在库的目的之外对其进行了修改,那么它将引起问题(甚至是setter方法,如果在RestTemplate实例化之后的任何时候调用它) -并且显然在被其他线程使用时,繁荣!)。Ross可能发生了什么(信誉不足,无法回答答案,但是我同时备份了线程安全和非线程安全参数)


0

好的,尽管我可能会从导致这些问题的源代码管理中挖掘出旧代码。

我认为可以说,即使在创建时进行同步,也存在其他线程可以修改内部集合的情况。所以最好小心。查看旧代码,是的,它实际上是在使用消息转换器。但仅在创建时同步。

restTemplate = new RestTemplate();

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

之后,与RestTemplate的唯一交互是与此:

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

这也是最终引发异常的行。

当然,与消息转换器没有任何交互(我们没有本地引用)。

查看stacktrace和spring源代码,在此行发生了错误:

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

那我们有什么呢?

  1. 我们可以同时访问messageConverters
  2. 如果我们的代码没有执行,那么哪个代码做了?我没有答案。当时我的解决方案是每次都创建一个新的RestTemplate,因为此应用程序不关心性能。

因此,总而言之,在某些情况下,事情可能不是线程安全的,当然,如果您直接使用消息转换器的话。这种情况虽然很奇怪,但是我认为发布它会很有用。


我的猜测:运行后restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());,请不要安全地发布对所做的更改restTemplate
Raedwald

也就是说,我认为您的调用restTemplate.getMessageConverters().add(...不是以线程安全的方式进行的。
Raedwald

-3

讨厌不同意上面接受的答案(添加了重点),但是不,这不是线程安全的。即使在创建之后。在内部它正在与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)

1
接受答案中的引号和链接来自Spring自己。因此可以安全地假设RestTemplate旨在实现线程安全,而未能达到线程安全将是一个错误。因此,在您的主张得到对已接受的Spring bug报告的引用的支持之前,我倾向于认为您犯了一个错误。例如,通过在设置后添加消息转换器。
Raedwald

嗯,一种有趣的方法,如果没有引起错误,那肯定是不正确的。我的朋友,这是软件的世界,各种各样的事情都可能出错,而且在许多公认的系统和库中确实存在错误。
罗斯

让我们仔细看看该堆栈跟踪。getForObject,通过Rest进行的标准调用。接下来看一下ArrayList和current并发ModificationException的用法。真的很自我解释。在进行有益的讨论时,我决定将所见内容添加到该线程中。让人们从中得到想要的东西。
罗斯

1
不,不自我解释为中的错误RestTemplate。异常为aConcurrentModificationException并不表示一旦构造RestTemplate后就不是线程安全的。这与您在构造对象之后更改对象或其包含的对象之一(例如消息转换器列表)的代码一致。RestTemplateArrayList
拉德瓦尔德

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.