【问题标题】:Is RestTemplate thread safe?RestTemplate 线程安全吗?
【发布时间】:2014-05-24 05:54:18
【问题描述】:

Spring RestTemplate 线程安全吗?那是

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

【问题讨论】:

标签: java spring rest thread-safety


【解决方案1】:

RestTemplateis thread safe(已添加重点):

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


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

如果您检查the source code of RestTemplate,您会发现它在构造对象后不使用synchronized 方法或volatile 字段来提供线程安全。所以在构造之后修改RestTemplate 对象是安全的。特别是添加消息转换器是不安全的。

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

  • 使用RestTemplate(List<HttpMessageConverter<?>> messageConverters) 构造函数。因为messageConverters的内部列表是final,所以这个safely publishes the list of message converters
  • 使用setMessageConverters(List<HttpMessageConverter<?>> messageConverters) mutator ,然后使用safely-publish 更改后的RestTemplate 对象。使用具有 <property name="messageConverters"><list>... 的 Spring bean 定义可以做到这一点,就像大多数实际用例中的 bean will be safely published by the thread setting up the container 一样。
  • getMessageConverters() 返回的引用上使用List.add,然后安全地发布更改后的RestTemplate 对象。但是,RestTemplate 的文档并未明确声明它返回可用于更改消息转换器列表的引用。当前实现确实如此,但可能会更改实现以返回Collections.unmodifiableList 或列表的副本。所以最好不要这样改变。

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

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

【讨论】:

  • 不仅它是线程安全的,而且它的创建似乎很昂贵。我最近在 tomcat 上运行主要性能问题,因为 RestTemplate 的初始化导致类加载器锁争用。
  • 我认为它的线程不安全。它使用消息转换器,您可以在运行时从两个不同的线程添加消息转换器。它会在执行 restTemplate.exchange() 调用时抛出并发修改异常,因为它会遍历此消息转换器。
  • @BorisTreukhov 我在调试时也注意到性能下降,所以我用谷歌搜索并找到了这个。
  • @comiventor 如果您不使用自定义 messageConverters,则可以使用 RestTemplate 的一个实例。否则,通过构造函数注入 messageConverters - 也是线程安全的。
  • “线程安全一旦构造”似乎意味着“线程安全,只要你不改变它的状态”,这是真的,因为 any 对象是线程只要其状态不改变就安全。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    好的,不过我可能会从源代码控制中挖掘出导致这些问题的旧代码。

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

    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 所做的更改。
    • 也就是说,我认为你的restTemplate.getMessageConverters().add(...的调用不是以线程安全的方式完成的。
    【解决方案4】:

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

    【讨论】:

    • 已接受答案中的引用和链接来自 Spring 本身。因此可以安全地假设 RestTemplate 旨在是线程安全的,而线程安全失败将是一个错误。因此,在您的声明得到对已接受的 Spring 错误报告的引用支持之前,我倾向于认为您犯了一个错误。例如,通过在设置后添加消息转换器。
    • 嗯,有趣的方法,如果不是作为错误提出,它一定是不正确的。我的朋友,这就是软件的世界,有各种各样的事情都可能出错,而且在许多公认的系统和库中确实存在错误。
    • 让我们仔细看看那个堆栈跟踪。 getForObject,一个通过 Rest 的标准调用。接下来看看ArrayList和concurrentModificationException的使用。真的很不言自明。在有用的讨论的情况下,我决定将我所看到的添加到这个线程中。让人们从中得到他们想要的东西。
    • 不,作为RestTemplate 中的一个错误,它不能自我解释。例外是ConcurrentModificationException 并不表示RestTemplate 一旦构造 就不是线程安全的。这与您的代码在构造对象后更改了RestTemplate 对象或其包含的ArrayList 对象之一(例如消息转换器列表)是一致的。
    猜你喜欢
    • 2021-05-20
    • 1970-01-01
    • 2020-04-15
    • 2011-07-04
    • 2014-04-26
    • 2012-11-30
    • 2010-12-30
    • 2013-03-12
    • 2021-08-03
    相关资源
    最近更新 更多