Java RestTemplate 线程安全吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22989500/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 19:48:21  来源:igfitidea点击:

Is RestTemplate thread safe?

javaspringrestthread-safety

提问by Raedwald

Is a Spring RestTemplatethread-safe? That is

SpringRestTemplate线程安全吗?那是

  • Is a RestTemplatea Strategy object that multiple connections can safely share. or
  • Is a RestTemplatea connection object (like a data-base connection), which can not be shared while in use, and requires creation afresh, or pooling, for each connection.
  • 是一个RestTemplateStrategy 对象,多个连接可以安全地共享。或者
  • 是一个RestTemplate连接对象(如数据库连接),在使用时不能共享,需要为每个连接重新创建或池化。

采纳答案by Raedwald

RestTemplateis thread safe(emphasis added):

RestTemplate是线程安全的(强调):

Conceptually, it is very similar to the JdbcTemplate, JmsTemplate, and the various other templates found in the Spring Framework and other portfolio projects. This means, for instance, that the RestTemplateis thread-safe once constructed

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



Objects of the RestTemplateclass do not change any of their state information to process HTTP: the class is an instance of the Strategy design pattern, rather than being like a connection object. With no state information, there is no possibility of different threads corrupting or racing state information if they share a RestTemplateobject. This is why it is possible for threads to share these objects.

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

If you examine the source code of RestTemplateyou will see that it does not use synchronizedmethods or volatilefields to provide thread-safety after construction of the object. So it is notsafe to modify a RestTemplateobject after construction. In particular, it is unsafe to add a message converter.

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

To provide it with a list of message converters you must do one of the following:

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

  • Use the RestTemplate(List<HttpMessageConverter<?>> messageConverters)constructor. As the internal list of messageConvertersis final, this safely publishes the list of message converters.
  • Use the setMessageConverters(List<HttpMessageConverter<?>> messageConverters)mutator andthen safely-publishthe changed RestTemplateobject. Using a Spring bean definition that has a <property name="messageConverters"><list>...does this, as the bean will be safely published by the thread setting up the containerin most practical use cases.
  • Use List.addon the reference returned by getMessageConverters()and then safely publish the changed RestTemplateobject. However, the documentation for RestTemplatedoes not explicitly state that it returns a reference that can be used to alter the list of message converters. The current implementation does, but possibly the implementation might be changed to return a Collections.unmodifiableListor a copy of the list. So it might be better not to change it this way.
  • 使用RestTemplate(List<HttpMessageConverter<?>> messageConverters)构造函数。由于messageConvertersis的内部列表final,这可以安全地发布消息转换器列表
  • 使用setMessageConverters(List<HttpMessageConverter<?>> messageConverters)mutator 然后安全地发布更改的RestTemplate对象。使用具有 a 的 Spring bean 定义可以<property name="messageConverters"><list>...做到这一点,因为在大多数实际用例中,bean将由设置容器的线程安全地发布
  • List.add在由返回的引用上使用getMessageConverters(),然后安全地发布更改的RestTemplate对象。但是, 的文档RestTemplate没有明确声明它返回可用于更改消息转换器列表的引用。当前的实现是这样,但可能会更改实现以返回Collections.unmodifiableList列表的一个或副本。所以最好不要以这种方式改变它。

Note that the first case is the only means of setting up the message converters when constructing the object, so it iscorrect to say that it "is thread safe once constructed".

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

The class is part of the Spring Framework, so in almost all practical cases objects of the class will be set up as part of a Spring Application Context, using the first (dependency injection using a constructor) or second (dependency injection using a setter) methods, and so would be guaranteed to be safely published to multiple threads.

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

回答by Ross

Hate to disagree with the accepted answer above (emphasis added), but no it is not Thread safe. Even after creation. Internally it is playing around with ArrayLists, I have not dug into the source. I have seen too many of these:

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

回答by Vasilis Nicolaou

It is thread safe from the library's point of view. For instance, the getMessageConverters() is public Which means that if someone gets hold on the list and modifies it outside of the purpose of the library then it will cause issues (and even the setter method, if it's called at any moment after RestTemplate instantiation - and while being used by other threads obviously, boom!). Which probably is what happened to Ross (not enough reputation to reply to the answer, but I'm backing up both the thread-safe and not thread-safe arguments)

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

回答by Ross

Alright, though I might dig the old code out from source control that caused these issues.

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

I think it would be fair to say that even synchronizing on creation there exist circumstances where another thread can modify the internal collections. So best be careful. Looking at the old code, yes it was actually using a message converter. But only when synchronized on creation.

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

restTemplate = new RestTemplate();

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

After that the only interaction with RestTemplate was with this:

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

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

This is also the line that eventually throws the exception.

这也是最终抛出异常的行。

There is of course no interaction with the message converter (we have no local reference to it).

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

Looking at the stacktrace, and the spring source code, the error occurred at this line:

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

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

So what do we have?

那么我们有什么?

  1. We have concurrent access to the messageConverters
  2. If our code didn't do it, then which code did? I don't have an answer. My solution at the time was to create a new RestTemplate every time as performance was not a concern in this app.
  1. 我们可以同时访问 messageConverters
  2. 如果我们的代码没有做到,那么哪个代码做到了?我没有答案。我当时的解决方案是每次都创建一个新的 RestTemplate,因为在这个应用程序中性能不是问题。

So in summary, there are circumstances where things may not be thread safe, certainly if you were to play around with message converters directly. This case though is a strange one, but I thought it would be useful to publish it.

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