Est RestTemplate thread-safe?

Est un Printemps RestTemplate thread-safe"? C'est-à-dire

  • est un RestTemplate un objet stratégique que plusieurs connexions peuvent partager en toute sécurité. ou
  • est un RestTemplate un objet de connexion (comme une connexion de base de données), qui ne peut pas être partagé en cours d'utilisation, et nécessite une nouvelle création, ou mise en commun, pour chaque connexion.
49
demandé sur Raedwald 2014-04-10 17:30:02

4 réponses

RestTemplate is thread safe (non souligné dans l'original):

conceptuellement, il est très similaire au JdbcTemplate , JmsTemplate , et les divers autres modèles trouvés dans le cadre du printemps et d'autres projets de portfolio. Cela signifie, par exemple, que le RestTemplate est sans fil une fois construit


objets de la RestTemplate class ne changent aucune de leurs informations d'État pour traiter HTTP: la classe est une instance du schéma de conception de la stratégie, plutôt que d'être comme un objet de connexion. Sans information d'état, il n'y a pas de possibilité de différents threads corrompant ou informations d'état de course si elles partagent un RestTemplate objet. C'est pourquoi il est possible pour les threads de partager ces objets.

si vous examinez le code source de RestTemplate vous verrez qu'il n'utilise pas les méthodes synchronized ou les champs volatile pour assurer la sécurité du fil après la construction de l'objet. Il est donc pas sûr de modifier un RestTemplate objet après la construction. En particulier, il est dangereux d'ajouter un convertisseur de messages.

pour lui fournir une liste de convertisseurs de messages, vous devez faire l'un des suivants:

  • utilisez le constructeur RestTemplate(List<HttpMessageConverter<?>> messageConverters) . Comme l' la liste interne de messageConverters est final , ce publie en toute sécurité la liste des convertisseurs de message .
  • utilisez le setMessageConverters(List<HttpMessageConverter<?>> messageConverters) mutateur et puis publier en toute sécurité l'objet modifié RestTemplate . L'utilisation d'une définition de haricot de printemps qui a un <property name="messageConverters"><list>... le fait, car le haricot sera publié en toute sécurité par le fil de configuration du conteneur dans la plupart des pratiques les cas d'utilisation.
  • utiliser List.add sur la référence retournée par getMessageConverters() et ensuite publier en toute sécurité l'objet modifié RestTemplate . Cependant, la documentation de RestTemplate n'indique pas explicitement qu'elle renvoie une référence qui peut être utilisée pour modifier la liste des convertisseurs de messages. La mise en œuvre actuelle Oui, mais peut-être la mise en œuvre pourrait être modifiée pour retourner un Collections.unmodifiableList ou une copie de la liste. Il vaut peut-être mieux ne pas le changer de cette façon.

notez que le premier cas est le seul moyen de mettre en place les convertisseurs de message lors de la construction de l'objet, donc il est correct de dire qu'il"est filé en toute sécurité une fois construit".

la classe fait partie du cadre du ressort, de sorte que dans presque tous les cas pratiques les objets de la classe seront configurés dans un contexte D'Application du ressort, en utilisant le premier (injection de dépendances à l'aide d'un constructeur) ou le second (injection de dépendances à l'aide d'un setter).

66
répondu Raedwald 2017-12-27 09:44:54

Il est thread-safe à partir de la bibliothèque de point de vue. Par exemple, le getMessageConverters() est public ce qui signifie que si quelqu'un s'empare de la liste et la modifie en dehors du but de la bibliothèque alors il causera des problèmes (et même la méthode setter, si elle est appelée à tout moment après instanciation RestTemplate - et tout en étant utilisée par d'autres threads évidemment, boom!). Ce qui est probablement ce qui est arrivé à Ross (pas assez de réputation pour répondre à la réponse, mais je suis sauvegarder à la fois le "thread-safe" et pas "thread-safe" arguments)

0
répondu Vasilis Nicolaou 2016-02-12 16:28:39

D'accord, bien que je puisse extraire l'ancien code du contrôle source qui a causé ces problèmes.

je pense qu'il serait juste de dire que même la synchronisation sur la création il existe des circonstances où un autre fil peut modifier les collections internes. Donc il vaut mieux être prudent. En regardant l'ancien code, oui il utilisait en fait un convertisseur de messages. Mais seulement lorsqu'il est synchronisé sur la création.

restTemplate = new RestTemplate();

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

après cela la seule interaction avec RestTemplate était avec ceci:

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

C'est aussi la ligne qui finit par jeter l'exception.

Il n'y a évidemment pas d'interaction avec le message convertisseur (nous n'avons pas de référence locale).

en regardant le stacktrace, et le code source du ressort, l'erreur s'est produite à cette ligne:

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

alors qu'est-ce qu'on a?

  1. nous avons concurrent accès aux convertisseurs de messages
  2. si notre code ne l'a pas fait, alors quel code l'a fait? Je n'ai pas de réponse. Ma solution à l'époque était de créer un nouveau RestTemplate à chaque fois que la performance n'était pas un problème dans cette application.

donc, en résumé, il ya des circonstances où les choses peuvent ne pas être thread sûr, certainement si vous deviez jouer avec les convertisseurs de messages directement. Ce cas est étrange, mais j'ai pensé qu'il serait utile de le publier.

-1
répondu Ross 2016-04-08 11:57:55

Hate d'être en désaccord avec la accepté la réponse ci-dessus (emphase ajoutée), mais non ce n'est pas Thread-safe. Même après la création. À l'intérieur, il joue avec les ArrayLists, Je n'ai pas creusé dans la source. J'en ai vu trop:

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)
-2
répondu Ross 2016-01-29 15:53:06