web-dev-qa-db-ja.com

RestTemplateはスレッドセーフですか?

Spring RestTemplateはスレッドセーフですか?あれは

  • RestTemplateは、複数の接続が安全に共有できるStrategyオブジェクトです。 または
  • RestTemplateは接続オブジェクト(データベース接続など)であり、使用中は共有できず、接続ごとに新たに作成またはプールする必要があります。
57
Raedwald

RestTemplateスレッドセーフです (強調を追加):

概念的には、JdbcTemplateJmsTemplate、およびSpring Frameworkやその他のポートフォリオプロジェクトにある他のさまざまなテンプレートに非常に似ています。これは、たとえば、RestTemplateは一度構築されるとスレッドセーフであることを意味します


RestTemplateクラスのオブジェクトは、HTTPを処理するために状態情報を変更しません。クラスは、接続オブジェクトのようなものではなく、Strategyデザインパターンのインスタンスです。状態情報がないため、異なるスレッドがRestTemplateオブジェクトを共有している場合、状態情報が破損または競合する可能性はありません。これが、スレッドがこれらのオブジェクトを共有できる理由です。

RestTemplateのソースコード を調べると、構築後にスレッドセーフを提供するためにsynchronizedメソッドまたはvolatileフィールドを使用していないことがわかります。オブジェクトの。したがって、構築後にRestTemplateオブジェクトを変更することはnot安全です。特に、メッセージコンバーターを追加することは安全ではありません。

メッセージコンバータのリストを提供するには、次のいずれかを実行する必要があります。

  • RestTemplate(List<HttpMessageConverter<?>> messageConverters)コンストラクターを使用します。 messageConvertersの内部リストはfinalなので、これは メッセージコンバーターのリストを安全に公開します です。
  • setMessageConverters(List<HttpMessageConverter<?>> messageConverters)ミューテーターandを使用してから、 safely-publish 変更されたRestTemplateオブジェクトを使用します。 Beanが コンテナを設定するスレッドによって安全に公開される として、_<property name="messageConverters"><list>..._を持つSpring Bean定義を使用してこれを行います。
  • getMessageConverters()によって返される参照で_List.add_を使用し、変更されたRestTemplateオブジェクトを安全に公開します。ただし、RestTemplateのドキュメントは、メッセージコンバーターのリストを変更するために使用できる参照を返すことを明示的に述べていません。現在の実装はサポートしていますが、_Collections.unmodifiableList_またはリストのコピーを返すように実装が変更される可能性があります。したがって、このように変更しない方が良いかもしれません。

最初のケースは、オブジェクトを構築するときにメッセージコンバータを設定する唯一の手段であるため、isは「スレッドセーフである」と言うのが正しいことに注意してください。一度構築」。

クラスはSpring Frameworkの一部であるため、ほとんどすべての場合、クラスのオブジェクトは、最初(コンストラクターを使用した依存性注入)または2番目(セッターを使用した依存性注入)を使用して、Spring Application Contextの一部として設定されますメソッドなど、複数のスレッドに安全に公開されることが保証されます。

78
Raedwald

ライブラリの観点からはスレッドセーフです。たとえば、getMessageConverters()はpublicです。つまり、誰かがリストを保持し、ライブラリの目的外でリストを変更すると、問題が発生します(RestTemplateのインスタンス化後の任意の時点で呼び出されると、setterメソッドも) -そして、明らかに他のスレッドで使用されている間、ブーム!)。これはおそらくRossに起こったことです(答えに答えるのに十分な評判はありませんが、スレッドセーフとスレッドセーフではない引数の両方をバックアップしています)

1

さて、これらの問題の原因となったソース管理から古いコードを掘り下げるかもしれませんが。

作成時に同期をとっても、別のスレッドが内部コレクションを変更できる状況が存在すると言ってもいいでしょう。気をつけてください。古いコードを見ると、はい、実際にはメッセージコンバーターを使用していました。ただし、作成時に同期する場合のみ。

restTemplate = new RestTemplate();

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

その後、RestTemplateとの唯一のやり取りはこれでした:

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

これは、最終的に例外をスローする行でもあります。

もちろん、メッセージコンバーターとの相互作用はありません(ローカルへの参照はありません)。

スタックトレースと春のソースコードを見ると、この行でエラーが発生しました:

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

では、何がありますか?

  1. MessageConvertersへの同時アクセスがあります
  2. 私たちのコードがそれをしなかった場合、どのコードがしましたか?答えがありません。当時の私のソリューションは、このアプリではパフォーマンスが問題にならないため、毎回新しいRestTemplateを作成することでした。

要約すると、メッセージコンバーターを直接操作する場合、確かに物事がスレッドセーフでない場合があります。このケースは奇妙なものですが、私はそれを公開することが有用だと思いました。

0
Ross