J’ai une application Web s’exécutant sur Tomcat.
Plusieurs calculs doivent être effectués à plusieurs endroits dans l’application Web. Puis-je effectuer ces calculs des fonctions d’assistance statiques? Si le serveur dispose de suffisamment de cœurs de processeur, plusieurs appels à cette fonction statique (résultant de plusieurs demandes adressées à différents servlets) peuvent-ils s’exécuter en parallèle? Ou une demande doit-elle attendre que l’autre demande termine l’appel?
public class Helper { public static void doSomething(int arg1, int arg2) { // do something with the args return val; } }
si les appels sont parallèles: j’ai une autre classe d’assistance avec des fonctions statiques, mais cette classe contient un membre statique privé qui est utilisé dans les fonctions statiques. Comment puis-je m’assurer que les fonctions sont thread-safe?
public class Helper { private static SomeObject obj; public static void changeMember() { Helper.obj.changeValue(); } public static Ssortingng readMember() { Helper.obj.readValue(); } }
changeValue()
et readValue()
lisent / changent la même variable membre de Helper.obj
. Dois-je synchroniser l’ensemble des fonctions statiques ou simplement le bloc où Helper.obj
est utilisé? Si je devais utiliser un bloc, quel object devrais-je utiliser pour le verrouiller?
Puis-je effectuer ces calculs des fonctions d’assistance statiques? si le serveur dispose de suffisamment de cœurs de processeur, plusieurs appels à cette fonction statique (résultant de plusieurs demandes adressées à différents servlets) peuvent-ils s’exécuter en parallèle?
Oui et oui.
dois-je rendre les fonctions statiques entières synchronisées
Ça marchera.
ou juste le bloc où
Helper.obj
est utilisé
Cela fonctionnera aussi.
si je devais utiliser un bloc, quel object devrais-je utiliser pour le verrouiller?
Utilisez un static Object
:
public class Helper { private static SomeObject obj; private static final Object mutex = new Object(); public static void changeMember() { synchronized (mutex) { obj.changeValue(); } } public static Ssortingng readMember() { synchronized (mutex) { obj.readValue(); } } }
Idéalement, cependant, vous écririez la classe d’assistance comme étant immuable (sans état ou autre) afin de ne pas vous soucier de la sécurité des threads.
Vous devez capturer les calculs dans une classe et créer une instance de la classe pour chaque thread. Ce que vous avez maintenant n’est pas threadsafe, comme vous le savez, et pour le rendre threadsafe, vous devrez vous synchroniser sur la ressource statique / les méthodes qui accèdent à cette ressource statique, ce qui entraînera le blocage.
Notez qu’il existe des modèles pour vous aider avec cela. Vous pouvez utiliser le modèle de stratégie (dans sa forme canonique, la stratégie doit être choisie au moment de l’exécution, ce qui peut ou non s’appliquer ici) ou une variante. Créez simplement une classe pour chaque calcul avec une méthode execute (et une interface contenant la méthode), puis passez un object context à exécuter. Le contexte contient tout l’état du calcul. Une instance de stratégie par thread, avec son contexte, et vous ne devriez pas avoir de problèmes.
Si vous n’avez pas à le partager, vous pouvez en faire un thread local, alors il n’est pas nécessaire qu’il soit thread-safe.
public class Helper { private static final ThreadLocal obj = new ThreadLocal () { public SomeObject initialValue() { return enw SomeObject(); } } public static void changeMember() { Helper.obj.get().changeValue(); } public static Ssortingng readMember() { Helper.obj.get().readValue(); } }
Je résumerai ici ce qui a été dit dans les commentaires sur la réponse de Matt Ball, puisqu’elle s’est assez longue à la fin et que le message est perdu: et le message était:
Dans un environnement partagé tel qu’un serveur Web / d’application, vous devez essayer très fort de trouver une solution sans synchronisation. L’utilisation d’assistants statiques synchronisés sur un object statique peut fonctionner assez bien pour une application autonome avec un seul utilisateur devant l’écran. Dans un scénario multi-utilisateur / multi-application, cela aboutirait très probablement à une performance très médiocre – cela signifierait effectivement la sérialisation de l’access à votre application, tous les utilisateurs devraient attendre sur le même verrou. Vous ne remarquerez peut-être pas le problème pendant longtemps: si les calculs sont suffisamment rapides et si la charge est répartie uniformément.
Mais tout à coup, tous vos utilisateurs pourraient essayer de faire le calcul à 9h et votre application s’arrêterait pour fonctionner! Je veux dire pas vraiment arrêter, mais ils bloquaient tous sur la serrure et faisaient une queue énorme.
Maintenant, indépendamment de la nécessité d’un état partagé, puisque vous avez initialement nommé les calculs comme sujet de synchronisation: leurs résultats doivent-ils être partagés? Ou ces calculs sont-ils spécifiques à un utilisateur / une session? Dans ce dernier cas, un ThreadLocal selon Peter Lawrey serait suffisant. Sinon, je dirais que pour les performances globales, il serait préférable de dupliquer les calculs pour tous ceux qui en ont besoin afin de ne pas se synchroniser (cela dépend du coût).
La gestion de session devrait également être mieux laissée au conteneur: il a été optimisé pour le gérer efficacement, y compris le regroupement, si nécessaire Mais comme Matt Ball l’a déclaré, il vaut mieux demander séparément.
Si vous êtes préoccupé par la synchronisation et la sécurité des threads, n’utilisez pas d’utilitaires statiques. Créez une classe normale avec vos méthodes d’assistance et créez une instance à la demande d’un servlet. Rester simple 🙂
Dans le premier cas, vous n’avez pas à vous soucier des problèmes de thread, car les variables sont locales à chaque thread. Cependant, vous identifiez correctement le problème dans le second cas, car plusieurs threads liront / écrivent le même object. La synchronisation sur les méthodes fonctionnera, de même que les blocs synchronisés.
Pour la première partie: Oui, ces appels sont indépendants et s’exécutent en parallèle lorsqu’ils sont appelés par différents threads.
Pour la dernière partie: Utilisez des blocs de synchronisation sur l’object simultané, un object factice ou un object de classe. Soyez conscient des blocs de synchronisation en cascade. Ils peuvent conduire à des serrures lorsque acquis dans un ordre différent.