提问人:Raedwald 提问时间:4/10/2014 更新时间:9/5/2019 访问量:55292
RestTemplate 线程安全吗?
Is RestTemplate thread safe?
问:
Spring 线程安全吗?那是RestTemplate
- 是多个连接可以安全共享的 Strategy 对象。或
RestTemplate
- 是一个连接对象(如数据库连接),在使用时不能共享,并且需要为每个连接重新创建或池化。
RestTemplate
答:
RestTemplate
线程安全吗(强调后加):
从概念上讲,它与 Spring Framework 和其他项目组合项目中的 、 和其他各种模板非常相似。这意味着,例如,一旦构造,
RestTemplate
就是线程安全的JdbcTemplate
JmsTemplate
类的对象不会更改其任何状态信息来处理 HTTP:该类是策略设计模式的实例,而不是像连接对象一样。在没有状态信息的情况下,如果不同的线程共享一个对象,则它们不可能损坏或竞动状态信息。这就是线程可以共享这些对象的原因。RestTemplate
RestTemplate
如果你检查 RestTemplate
的源代码,你会发现它在构造对象后不使用方法或字段来提供线程安全。因此,在构造后修改对象是不安全的。特别是,添加消息转换器是不安全的。synchronized
volatile
RestTemplate
若要为其提供消息转换器列表,必须执行下列操作之一:
- 使用构造函数。由于内部列表是 ,这将安全地发布消息转换器列表。
RestTemplate(List<HttpMessageConverter<?>> messageConverters)
messageConverters
final
- 使用赋值函数,然后安全地发布更改的对象。使用具有 a 的 Spring bean 定义可以做到这一点,因为在大多数实际用例中,设置容器的线程将安全地发布 bean。
setMessageConverters(List<HttpMessageConverter<?>> messageConverters)
RestTemplate
<property name="messageConverters"><list>...
- 对返回的引用使用,然后安全地发布更改的对象。但是,文档并未明确说明它返回可用于更改消息转换器列表的引用。当前实现可以,但可能会更改实现以返回列表的副本或副本。因此,最好不要以这种方式更改它。
List.add
getMessageConverters()
RestTemplate
RestTemplate
Collections.unmodifiableList
请注意,第一种情况是在构造对象时设置消息转换器的唯一方法,因此可以说它“一旦构造就对线程安全”。
该类是 Spring Framework 的一部分,因此在几乎所有实际情况下,该类的对象都将设置为 Spring Application Context 的一部分,使用第一种方法(使用构造函数的依赖注入)或第二种(使用 setter 的依赖注入)方法,因此可以保证安全地发布到多个线程。
评论
讨厌不同意上面公认的答案(强调后加),但不,它不是线程安全的。即使在创建之后。在内部,它正在玩弄 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)
评论
RestTemplate
ConcurrentModificationException
RestTemplate
RestTemplate
ArrayList
synchonized
关键字。
从库的角度来看,它是线程安全的。例如,getMessageConverters() 是公共的,这意味着如果有人抓住了列表并在库的目的之外对其进行了修改,那么它会导致问题(甚至是 setter 方法,如果它在 RestTemplate 实例化后的任何时候被调用 - 并且显然被其他线程使用,繁荣!这可能就是发生在罗斯身上的事情(没有足够的声誉来回答答案,但我正在支持线程安全和非线程安全的论点)
好吧,尽管我可能会从导致这些问题的源代码管理中挖掘出旧代码。
我认为可以公平地说,即使在创建时同步,也存在另一个线程可以修改内部集合的情况。所以最好小心。 查看旧代码,是的,它实际上是在使用消息转换器。但只有在创建时同步时。
restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
在那之后,与 RestTemplate 的唯一交互是这样的:
return restTemplate.postForObject(url, object, clazz);
这也是最终引发异常的行。
当然,没有与消息转换器的交互(我们没有对它的本地引用)。
查看堆栈跟踪和 spring 源代码,错误发生在以下行:
for (HttpMessageConverter<?> converter : getMessageConverters()) {
那么我们有什么呢?
- 我们可以并发访问 messageConverters
- 如果我们的代码没有这样做,那么哪个代码做到了?我没有答案。我当时的解决方案是每次都创建一个新的 RestTemplate,因为性能不是这个应用程序的问题。
因此,总而言之,在某些情况下,事情可能不是线程安全的,当然,如果您要直接使用消息转换器。这个案例虽然很奇怪,但我认为发布它会很有用。
评论
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate
restTemplate.getMessageConverters().add(...
评论