5
votes

Comment utiliser XPath d'une manière sûre et efficace pour les threads?

(Une question similaire a été posée ici, Java XPathFactory thread-safety , mais le La réponse donnée est incorrecte car elle ignore le fait que la documentation indique que XPathFactory.newInstance () n'est pas thread-safe.)

À partir de XPathFactory Javadoc , nous avons:

La classe XPathFactory n'est pas thread-safe. En d'autres termes, c'est le la responsabilité de l'application de s'assurer qu'au plus un thread est en utilisant un objet XPathFactory à un moment donné. Les implémentations sont encouragés à marquer les méthodes comme synchronisées pour se protéger clients cassés.

XPathFactory n'est pas rentrant. Alors que l'une des méthodes newInstance est en cours d'appel, les applications ne peuvent pas tenter d'appeler récursivement un méthode newInstance, même à partir du même thread.

Donc, d'après la citation ci-dessus, je suppose que XPathFactory.newInstance () (une méthode statique) ne doit pas être appelée simultanément. Ce n'est pas thread-safe.

La fabrique renvoie des objets XPath, qui ont ce XPath Javadoc :

Un objet XPath n'est ni thread-safe ni réentrant. En d'autres termes, il est de la responsabilité de l'application de s'assurer qu'un XPath l'objet n'est pas utilisé à partir de plus d'un thread à un moment donné, et tandis que la méthode d'évaluation est invoquée, les applications peuvent ne pas être récursives appelez la méthode d'évaluation.

D'après la citation ci-dessus, je suppose que XPath.evaluate et XPathExpression.evaluate ne doivent pas être appelés simultanément. Ils ne sont pas compatibles avec les threads.

Normalement, lorsque je travaille avec des classes qui ne sont pas thread-safe, j'utilise juste des variables locales, mais parce que XPathFactory.newInstance () n'est pas thread-safe, et c'est une méthode statique , Je ne sais pas comment l'utiliser de manière sûre et efficace. Je suppose que je pourrais synchroniser les appels vers newInstance , mais je m'inquiète des performances car mon application est une application de routage de messages XML. (Dans mes tests de fumée de newInstance , cela prend environ 0,4 millisecondes.)

Je ne trouve aucun exemple d'utilisation de Java XPath d'une manière thread-safe, et je ne suis pas sûr de savoir comment utiliser XPath d'une manière thread-safe mais efficace. J'ai également la contrainte dont j'ai besoin pour utiliser XPath à l'intérieur d'un singleton (en particulier un Apache Camel Processor ).


6 commentaires

Pour info, le plus proche d'un exemple que j'ai trouvé est celui d'Apache Camel XPathBuilder , qui stipule que" Cette implémentation est thread-safe en utilisant les threads locaux et le pooling pour permettre la concurrence ", mais l'implémentation semble étroitement liée à Camel et certaines parties sont au-dessus de ma tête.


Re, "Je pourrais synchroniser les appels vers newInstance , mais je m'inquiète pour les performances." Avez-vous mesuré les performances? L'étape 1 (pour moi, en tout cas) est toujours de faire quelque chose de facile qui fonctionne. L'étape 2 consiste à évaluer si cela fonctionne assez bien . Si la réponse à l'étape 2 est «oui», le problème est résolu.


javax.xml.xpath.XPathFactory.newInstance () crée une nouvelle usine . De combien d'usines XPath distinctes votre application a-t-elle besoin?


Vous semblez avoir mal compris la réentrance. XPath n'est pas du tout threadsafe et de plus, le même thread ne peut pas appeler évaluer pendant une évaluation.


@SolomonSlow Je n'ai pas mesuré les performances, et je sais que je devrais. Mais juste pour cette question, je suppose que les appels à newInstance sont chers . En ce qui concerne le nombre d'usines distinctes dont j'ai besoin, je traite les messages XML SOAP dans un pipeline à l'aide de composants CXF déployés sur Tomcat, il existe donc un pool de threads configurable. Je suppose que pour atteindre une concurrence maximale, j'aurais besoin d'usines et d'expressions égales au pool de threads, mais cela entre dans une réponse potentielle compliquée à cette question dont je ne suis pas sûr.


@BoristheSpider Je suppose que votre commentaire est adressé à Solomon et non à moi, l'OP, car c'est exactement le sujet de cette question: XPath n'est pas thread-safe.


3 Réponses :


0
votes

La documentation semble se concentrer sur les objets, pas sur les appels newInstance. Voici les parties que vous avez citées; J'ai ajouté un peu d'emphase:

La classe XPathFactory n'est pas thread-safe. En d'autres termes, il est de la responsabilité de l'application de s'assurer qu'au plus un thread utilise un objet XPathFactory à un moment donné.

Et:

Un objet XPath n'est ni thread-safe ni réentrant.

Notez qu'il fait référence à objets existants. XPathFactory.newInstance est une méthode statique; il ne fonctionne pas sur un objet XPathFactory existant. Au moment où il est appelé, il n'y a aucun objet à partager potentiellement entre les threads.

La documentation indique que les méthodes non statiques des objets XPath et XPathFactory existants ne sont pas thread-safe. Cela ne veut pas dire que vous ne pouvez pas les créer dans différents threads, mais seulement que vous ne pouvez pas utiliser un objet dans différents threads (sauf si vous protégez cette utilisation avec la synchronisation ou les verrous).

méthode newInstance à trois arguments avec le nom d'une classe d'usine qui tente elle-même d'appeler newInstance. < / p>


2 commentaires

Vous semblez ignorer la partie qui dit " Pendant que l'une des méthodes newInstance est appelée, les applications ne peuvent pas tenter d'appeler récursivement une méthode newInstance, même à partir du même thread. " Cela semble explicitement déclarez que ces méthodes statiques newInstance ne sont pas thread-safe.


Bon point. Le mot «récursivement» dans ce paragraphe est intéressant. Je suppose que cela signifie que l'usine spécifiée par le paramètre factoryClassName ne peut pas invoquer d'autres méthodes XPathFactory.newInstance. Mais, juste pour être sûr, vous pouvez protéger tous les appels à XPathFactory.newInstance avec un verrouillage commun ou une synchronisation sur un objet commun.



4
votes

Je suppose que XPathFactory.newInstance () (une méthode statique) ne doit pas être appelée simultanément. Ce n'est pas thread-safe.

Cette documentation pourrait être interprétée différemment: la sécurité des threads et la réentrance sont des attributs distincts, il est donc possible que XPathFactory.newInstance () soit thread-safe mais non réentrant. Le mot récursivement semble clé; mais la structure de la phrase est difficile à analyser. Sans passer en revue le code dans les moindres détails, la synchronisation autour des appels newInstance semble le seul moyen sûr de les utiliser. Notez que Java 9 a ajouté newDefaultInstance , qui semble trivialement thread-safe.

Je suppose que XPath.evaluate et XPathExpression.evaluate ne doivent pas être appelés simultanément. Ils ne sont pas thread-safe.

D'accord. La documentation indique clairement que ces méthodes ne sont ni thread-safe ni réentrantes.


2 commentaires

Il est utile de noter que la sécurité des threads et la rentrée sont des attributs distincts, mais je ne comprends pas comment nous pourrions "invoquer récursivement une méthode newInstance", d'autant plus que c'est une méthode statique. Le guillemet complet dit "Pendant que l'une des méthodes newInstance est appelée, les applications ne peuvent pas tenter d'appeler récursivement une méthode newInstance, même à partir du même thread " (c'est moi qui souligne). Étant donné que l'exception prouve la règle, «même à partir du même thread» signifie également «à partir de n'importe quel thread», et étant donné qu'il s'agit d'une méthode statique , nous ne pouvons pas l'utiliser simultanément.


J'ai révisé ma réponse. La documentation est ambiguë et le code source est alambiqué. Java 9 semble avoir résolu le problème avec une nouvelle méthode.



4
votes

Vous pouvez abandonner le monde JAXP / DOM et passer à Saxon, où le multithreading est beaucoup plus soigneusement intégré dans la conception de l'API:

  • Contrairement au DOM, les implémentations d'arbres natifs de Saxon sont thread-safe une fois la construction du document terminée. Vous obtenez également XPath 3.1 en bonus.

  • Un XPathCompiler est thread-safe une fois configuré en utilisant ses méthodes setter

  • Une XPathExpression (créée en compilant une expression à l'aide d'un XPathCompiler) est thread-safe (elle peut être exécutée simultanément dans plusieurs threads)

  • Un XPathSelector (formé en chargeant une XPathExpression) n'est pas thread-safe; il ne doit être évalué qu'une seule fois.


1 commentaires

Merci pour la recommandation, Michael. J'ai déjà lu des suggestions similaires du type "C'est dommage que (JAXP / DOM) soit populaire car il existe de meilleures alternatives." Je vais enquêter sur cela et le mettre dans ma poche arrière pour le moment car je suis investi dans la compréhension du fonctionnement de l'implémentation fournie.