RestTemplate 线程安全吗?

Is RestTemplate thread safe?

提问人:Raedwald 提问时间:4/10/2014 更新时间:9/5/2019 访问量:55292

问:

Spring 线程安全吗?那是RestTemplate

  • 是多个连接可以安全共享的 Strategy 对象。RestTemplate
  • 是一个连接对象(如数据库连接),在使用时不能共享,并且需要为每个连接重新创建或池化。RestTemplate
Java Spring Rest 线程安全

评论

0赞 Raedwald 4/10/2014
另请参阅 stackoverflow.com/questions/21242812/...
0赞 Raedwald 4/10/2014
另请参阅 stackoverflow.com/questions/21145558/...
0赞 Raedwald 4/11/2014
另请参阅 stackoverflow.com/questions/6747478/...

答:

111赞 Raedwald 4/10/2014 #1

RestTemplate 线程安全吗(强调后加):

从概念上讲,它与 Spring Framework 和其他项目组合项目中的 、 和其他各种模板非常相似。这意味着,例如,一旦构造,RestTemplate 就是线程安全JdbcTemplateJmsTemplate


类的对象不会更改其任何状态信息来处理 HTTP:该类是策略设计模式的实例,而不是像连接对象一样。在没有状态信息的情况下,如果不同的线程共享一个对象,则它们不可能损坏或竞动状态信息。这就是线程可以共享这些对象的原因。RestTemplateRestTemplate

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

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

  • 使用构造函数。由于内部列表是 ,这将安全地发布消息转换器列表RestTemplate(List<HttpMessageConverter<?>> messageConverters)messageConvertersfinal
  • 使用赋值函数然后安全地发布更改的对象。使用具有 a 的 Spring bean 定义可以做到这一点,因为在大多数实际用例中,设置容器的线程将安全地发布 bean。setMessageConverters(List<HttpMessageConverter<?>> messageConverters)RestTemplate<property name="messageConverters"><list>...
  • 对返回的引用使用,然后安全地发布更改的对象。但是,文档并未明确说明它返回可用于更改消息转换器列表的引用。当前实现可以,但可能会更改实现以返回列表的副本或副本。因此,最好不要以这种方式更改它。List.addgetMessageConverters()RestTemplateRestTemplateCollections.unmodifiableList

请注意,第一种情况是在构造对象时设置消息转换器的唯一方法,因此可以说它“一旦构造就对线程安全”。

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

评论

11赞 Boris Treukhov 3/11/2015
它不仅是线程安全的,而且它的创建似乎很昂贵。我最近在 tomcat 上运行了主要的性能问题,因为 RestTemplate 的初始化导致了类加载器锁争用。
3赞 sakura 6/9/2015
我不认为它的线程安全。它使用 messageconverters,您可以在运行时从两个不同的线程添加 messageconverters。它会在执行 restTemplate.exchange() 调用时抛出 concurrentmodificationexception,因为它会遍历此 messageconverters。
3赞 Alfredo Osorio 7/8/2015
@BorisTreukhov 我还注意到在调试时性能受到影响,所以我用谷歌搜索了一下并找到了这个。
3赞 user1697575 6/12/2018
@comiventor 如果您不使用自定义 messageConverters,那么可以使用一个 RestTemplate 实例。否则,通过构造函数注入 messageConverters - 也是线程安全的。
6赞 Mzzl 2/9/2019
“线程安全一旦构造”似乎意味着“只要您不更改其状态,线程安全”,从某种意义上说,只要任何对象的状态不改变,都是线程安全的。
-4赞 Ross 1/29/2016 #2

讨厌不同意上面公认的答案(强调后加),但不,它不是线程安全的。即使在创建之后。在内部,它正在玩弄 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赞 Raedwald 1/30/2016
接受的答案中的引用和链接来自Spring本身。因此,可以安全地假设 RestTemplate 是线程安全的,而线程安全失败将是一个错误。因此,在您的声明得到公认的 Spring 错误报告的引用支持之前,我倾向于认为您犯了一个错误。例如,通过在设置后添加消息转换器。
0赞 Ross 1/31/2016
嗯,有趣的方法,如果不是作为错误提出,那一定是不正确的。我的朋友,这是软件的世界,有各种各样的事情可能会出错,而且许多公认的系统和库中确实存在错误。
0赞 Ross 1/31/2016
让我们仔细看看该堆栈跟踪。getForObject,通过 Rest 进行的标准调用。接下来,看看 ArrayList 和 concurrentModificationException 的用法。真的不言自明。在进行有用的讨论时,我决定将我所看到的内容添加到此线程中。让人们从中做出他们想要的东西。
1赞 Raedwald 3/10/2016
不,不言自明,因为 .异常是 a 并不表示一旦构造就不是线程安全的。这与在构造对象后更改对象或其包含的对象之一(例如消息转换器列表)的代码是一致的。RestTemplateConcurrentModificationExceptionRestTemplateRestTemplateArrayList
2赞 Vasilis Nicolaou 2/13/2016 #3

从库的角度来看,它是线程安全的。例如,getMessageConverters() 是公共的,这意味着如果有人抓住了列表并在库的目的之外对其进行了修改,那么它会导致问题(甚至是 setter 方法,如果它在 RestTemplate 实例化后的任何时候被调用 - 并且显然被其他线程使用,繁荣!这可能就是发生在罗斯身上的事情(没有足够的声誉来回答答案,但我正在支持线程安全和非线程安全的论点)

0赞 Ross 4/8/2016 #4

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

我认为可以公平地说,即使在创建时同步,也存在另一个线程可以修改内部集合的情况。所以最好小心。 查看旧代码,是的,它实际上是在使用消息转换器。但只有在创建时同步时。

restTemplate = new RestTemplate();

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

在那之后,与 RestTemplate 的唯一交互是这样的:

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

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

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

查看堆栈跟踪和 spring 源代码,错误发生在以下行:

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

那么我们有什么呢?

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

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

评论

0赞 Raedwald 4/8/2016
我的猜测:在您运行但未安全地发布对 .restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());restTemplate
0赞 Raedwald 4/8/2016
也就是说,我认为您的调用不是以线程安全的方式完成的。restTemplate.getMessageConverters().add(...