Servlets Java : instances, sessions et multithreading expliqués simplement

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi les servlets font-ils encore peur, et comment y faire face ?

\n\n

Si vous avez déjà cherché sur Stack Overflow une réponse à la question « Comment fonctionnent les servlets ? », vous êtes sûrement tombé sur le légendaire fil de discussion avec des milliers de vues. La question qui revient sans cesse : « Combien d'instances d'un servlet sont créées ? Comment partager des données entre les requêtes ? Et pourquoi ma variable se comporte-t-elle comme une folle ? »

\n\n

Soyons honnêtes : les servlets, ce n'est pas du rocket science, mais il y a pas mal de pièges. Surtout quand il s'agit de multithreading et de variables partagées. Dans cet article, nous ne nous contenterons pas de paraphraser la documentation. Nous allons prendre un exemple concret, le décortiquer et vous montrer comment éviter les erreurs courantes. C'est parti !

\n\n

Comment un servlet arrive en mémoire : instanciation et cycle de vie

\n\n

Commençons par le commencement : combien d'instances d'un servlet sont créées ? La réponse : une seule. Oui, vous avez bien lu. Le conteneur de servlets (Tomcat, Jetty, etc.) crée exactement une instance de chaque servlet pour l'ensemble de l'application. Et cette instance vit jusqu'à ce que l'application soit déchargée.

\n\n

Voici à quoi ressemble un cycle de vie typique :

\n\n
    \n
  • init() — appelée une fois lors du chargement du servlet. Vous y initialisez les connexions à la base de données, les configurations, etc.
  • \n
  • service() — appelée pour chaque requête entrante. C'est ici que le traitement a lieu.
  • \n
  • destroy() — appelée avant le déchargement du servlet. Vous nettoyez les ressources.
  • \n
\n\n

Point clé : service() (et ses dérivés doGet, doPost) est exécuté dans un thread distinct pour chaque requête. Cela signifie que la même instance de servlet traite des centaines de requêtes simultanément. Et c'est là que les choses deviennent intéressantes.

\n\n

Exemple : un servlet, plusieurs threads

\n\n
@WebServlet("/counter")\npublic class CounterServlet extends HttpServlet {\n    private int counter = 0;  // variable partagée !\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        counter++;\n        resp.getWriter().write("Compteur : " + counter);\n    }\n}
\n\n

Si vous exécutez ce code et envoyez 10 requêtes en parallèle, vous n'obtiendrez pas les nombres de 1 à 10 dans l'ordre. Vous verrez plutôt quelque chose comme : 2, 5, 3, 8, 1... Ou même le counter pourrait sauter des valeurs. Pourquoi ? Parce que counter++ n'est pas une opération atomique. Deux threads peuvent lire la même valeur, l'incrémenter et la réécrire, perdant ainsi une incrémentation.

\n\n

Conclusion : les champs d'instance d'un servlet sont des variables partagées. Ils sont visibles par tous les threads. Et si vous ne synchronisez pas l'accès, vous obtiendrez une condition de concurrence (race condition).

\n\n

Sessions : comment ne pas mélanger les utilisateurs

\n\n

Imaginez maintenant : vous voulez stocker des données propres à chaque utilisateur (panier d'achat, identifiant, préférences). Pour cela, Java Servlets propose HttpSession. Une session est créée pour chaque nouvel utilisateur et vit jusqu'à ce qu'il ferme son navigateur ou que le délai d'expiration soit atteint.

\n\n

La session est une mémoire non partagée. Chaque utilisateur reçoit sa propre instance de session. Mais à l'intérieur de la session, vous pouvez stocker n'importe quel objet, et ils ne seront accessibles qu'à cet utilisateur.

\n\n

Exemple : travailler avec une session

\n\n
@WebServlet("/session-demo")\npublic class SessionServlet extends HttpServlet {\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) \n            throws ServletException, IOException {\n        HttpSession session = req.getSession();\n        Integer visitCount = (Integer) session.getAttribute("visitCount");\n        if (visitCount == null) {\n            visitCount = 0;\n        }\n        visitCount++;\n        session.setAttribute("visitCount", visitCount);\n        resp.getWriter().write("Nombre de visites : " + visitCount);\n    }\n}
\n\n

Contrairement à l'exemple précédent, ici chaque utilisateur a son propre compteur de visites. Pas de problème de concurrence !

\n\n

ServletContext vs HttpSession : quand utiliser quoi ?

\n\n

Beaucoup de débutants confondent ServletContext et HttpSession. Voici la différence :

\n\n
    \n
  • ServletContext : partagé par tous les utilisateurs de l'application. Idéal pour les données globales (configuration, pool de connexions).
  • \n
  • HttpSession : propre à chaque utilisateur. Parfait pour les données spécifiques à une session (panier, préférences).
  • \n
\n\n

Règle d'or : si vous devez partager des données entre tous les utilisateurs, utilisez ServletContext. Si les données sont propres à un utilisateur, utilisez HttpSession. Et si les données sont spécifiques à une seule requête, utilisez les attributs de la requête (HttpServletRequest).

\n\n

Multithreading et sécurité : les bonnes pratiques

\n\n

Pour éviter les problèmes de thread safety dans les servlets, suivez ces règles :

\n\n
    \n
  1. Évitez les variables d'instance mutables dans les servlets. Si vous devez en utiliser, synchronisez l'accès ou utilisez des classes thread-safe (comme AtomicInteger).
  2. \n
  3. Utilisez les variables locales dans les méthodes doGet/doPost. Chaque thread a sa propre pile, donc les variables locales sont automatiquement thread-safe.
  4. \n
  5. Préférez l'immutabilité : si un objet ne change pas d'état, il est intrinsèquement thread-safe.
  6. \n
  7. Pour les ressources partagées (base de données, fichiers), utilisez des pools de connexions et des mécanismes de synchronisation appropriés.
  8. \n
\n\n

En suivant ces principes, vous pouvez écrire des servlets robustes et performants, même sous forte charge.

\n\n

Conclusion

\n\n

Les servlets Java ne sont pas si effrayants une fois qu'on comprend leur cycle de vie et les implications du multithreading. Retenez les points clés : une seule instance par servlet, des sessions pour isoler les utilisateurs, et une attention particulière aux variables partagées. Avec ces bases, vous êtes prêt à développer des applications web Java fiables et évolutives.

\n\n

Vous avez des questions ou des retours ? Laissez un commentaire ci-dessous !

Blogs

Book Recommendations