Skip to content

Latest commit

 

History

History
131 lines (80 loc) · 10.2 KB

README.md

File metadata and controls

131 lines (80 loc) · 10.2 KB

🔝 Retour au Sommaire

8. Concurrence et parallélisme ⭐ 🔄

Partie 4 — Concurrence, E/S et données
Prérequis : programmation orientée objet (chapitre 3), lambdas, collections et Stream (chapitre 4).
Niveau : intermédiaire à confirmé.

Une application moderne ne fait presque jamais une seule chose à la fois. Un service web répond à des milliers de requêtes en parallèle, un traitement de données exploite tous les cœurs de la machine, une interface graphique reste réactive pendant qu'un calcul tourne en arrière-plan. Savoir faire plusieurs choses en même temps — proprement, sans corrompre les données et sans bloquer inutilement — est l'une des compétences qui séparent le code de démonstration du code de production.

Ce chapitre est marqué 🔄 (mis à jour) pour une bonne raison : la concurrence en Java a profondément changé ces dernières années. L'arrivée des virtual threads (Project Loom) bouleverse les habitudes prises depuis vingt ans. Nous enseignons donc les deux modèles — le modèle « classique » à base de pools de threads, toujours présent partout, et le modèle moderne — en expliquant quand utiliser l'un ou l'autre.


Concurrence ou parallélisme ?

Ces deux mots sont souvent confondus. Ils décrivent pourtant des idées distinctes — l'une concerne la structure d'un programme, l'autre son exécution.

Concurrence Parallélisme
Question Comment organiser plusieurs tâches qui progressent indépendamment ? Comment exécuter plusieurs calculs au même instant ?
Nature Structure du programme Exécution physique
Matériel Possible même sur un seul cœur (entrelacement) Nécessite plusieurs cœurs
Objectif typique Ne pas bloquer en attendant (E/S, réseau) Aller plus vite en répartissant le calcul

L'image du café résume bien la différence. Un seul barista qui jongle entre plusieurs commandes — il lance un café, prépare un thé pendant que la machine chauffe, encaisse un client en attendant — fait de la concurrence : une seule personne, plusieurs tâches en cours. Deux baristas qui préparent deux commandes exactement en même temps font du parallélisme.

Les deux notions sont liées mais indépendantes : un programme peut être concurrent sans être parallèle (entrelacement sur un cœur), et le parallélisme se construit le plus souvent au-dessus de mécanismes concurrents. Garder cette distinction en tête évite beaucoup de malentendus dans la suite.


Pourquoi c'est central en 2026

Trois réalités rendent ce sujet incontournable :

  • Le multicœur est la norme. Même un ordinateur portable d'entrée de gamme possède plusieurs cœurs. Un programme purement séquentiel laisse l'essentiel de la machine inutilisé.
  • Les applications sont massivement orientées E/S. Microservices, appels réseau, requêtes en base, files de messages : l'essentiel du temps est passé à attendre, pas à calculer. Bien gérer cette attente, c'est servir bien plus de requêtes avec les mêmes ressources.
  • Les virtual threads démocratisent la haute concurrence. Ce qui demandait hier une expertise pointue (programmation asynchrone, callbacks, reactive) devient accessible avec un modèle de code beaucoup plus simple à lire et à déboguer.

Deux familles de problèmes

Avant d'écrire la moindre ligne, il faut savoir à quel type de problème on a affaire, car la bonne solution n'est pas la même.

  • Les tâches bloquées par les E/S (I/O-bound) passent leur temps à attendre une réponse : réseau, disque, base de données. Ici, le but est de ne pas immobiliser un thread pendant l'attente. C'est le terrain de jeu idéal des virtual threads.
  • Les tâches gourmandes en calcul (CPU-bound) occupent réellement le processeur : traitement d'images, agrégations, calcul numérique. Ici, la performance est plafonnée par le nombre de cœurs ; on cherche à répartir le travail (flux parallèles, fork/join). Lancer des milliers de threads n'apporte rien — il n'y a pas plus de cœurs pour les exécuter.

Confondre les deux mène aux deux erreurs les plus classiques : noyer un service réseau sous un pool de threads trop petit, ou croire qu'on accélère un calcul en multipliant les threads au-delà du nombre de cœurs.


Une évolution majeure : Project Loom 🆕

Pendant des années, un thread Java était une ressource coûteuse du système d'exploitation. On ne pouvait pas en créer des dizaines de milliers ; il fallait les mutualiser (pools de threads) et, pour tenir la charge sans bloquer, écrire du code asynchrone — souvent verbeux et difficile à suivre.

Les virtual threads (threads virtuels), finalisés dans Java 21, renversent cette contrainte. Ce sont des threads légers, gérés par la JVM et non par le système : on peut en lancer des millions. Le modèle « un thread par tâche », longtemps déconseillé, redevient viable — et l'on peut écrire du code bloquant, simple et linéaire tout en obtenant l'efficacité d'un modèle asynchrone.

⚠️ À nuancer. Les virtual threads suppriment une contrainte — le coût d'un thread —, pas la difficulté de fond. Le partage d'état mutable, les race conditions et les problèmes de visibilité mémoire restent entiers. C'est pourquoi la synchronisation (8.6) et les collections concurrentes (8.7) demeurent aussi essentielles qu'avant.

Deux points d'actualité précis, approfondis dans les sections concernées :

  • Le pinning sur les blocs synchronized — qui pouvait neutraliser l'intérêt des virtual threads — est résolu depuis Java 24 (JEP 491). Une vigilance résiduelle subsiste néanmoins (code natif, certaines opérations bloquantes).
  • La concurrence structurée (StructuredTaskScope), remaniée dans Java 25, reste en preview (JEP 505) : c'est une approche prometteuse, à connaître, mais dont l'API peut encore évoluer.

Ce que vous allez apprendre

À l'issue de ce chapitre, vous saurez :

  • distinguer concurrence et parallélisme, et identifier si un problème est I/O-bound ou CPU-bound ;
  • créer et piloter des threads, puis déléguer leur gestion à un ExecutorService ;
  • composer des traitements asynchrones avec CompletableFuture ;
  • exploiter les virtual threads pour des services à forte charge d'E/S, et comprendre ce qu'apporte la concurrence structurée ;
  • garantir la sûreté d'un code concurrent (synchronisation, Lock, Atomic*, volatile) et choisir les bonnes collections concurrentes ;
  • déboguer un programme multithread dans Eclipse.

Plan du chapitre

Le chapitre suit une progression en quatre temps : le modèle classique (8.1 → 8.3), le modèle moderne issu de Loom (8.4 → 8.5), les fondations de la sûreté valables dans tous les cas (8.6 → 8.7), puis l'outillage (8.8).

Le modèle classique

Le modèle moderne ⚡ 🆕

Les fondations de la sûreté

L'outillage 🔧


Un avertissement nécessaire ⚠️

La concurrence est l'un des domaines où Java est le plus puissant — et le plus impitoyable. Un programme concurrent peut fonctionner parfaitement mille fois, puis échouer la mille-et-unième sur une machine plus chargée, sans qu'aucune ligne n'ait changé. Les bugs y sont non déterministes : race conditions, interblocages (deadlocks), problèmes de visibilité mémoire. Ils sont difficiles à reproduire et donc difficiles à corriger.

La bonne nouvelle, c'est qu'il existe des règles claires et des outils éprouvés pour les éviter. Tout au long du chapitre, nous privilégions systématiquement les abstractions de haut niveau (exécuteurs, virtual threads, collections concurrentes) aux manipulations bas niveau, parce qu'elles sont plus sûres par construction. La règle d'or à garder en tête dès maintenant : le code le plus sûr est celui qui ne partage pas d'état mutable.


Et dans Eclipse 🔧

Eclipse offre un réel confort pour le code concurrent. La vue Debug affiche tous les threads actifs et leur état, on peut poser des points d'arrêt déclenchés par un thread précis, suspendre les threads individuellement, et inspecter les piles d'appels en parallèle. Ces fonctionnalités sont détaillées en section 8.8 ; les sections précédentes y feront référence dès qu'un comportement mérite d'être observé pas à pas.


⬅️ Partie 3 — Tests et qualité du code · Sommaire · Commencer : 8.1 Pourquoi la concurrence ➡️

⏭ Pourquoi la concurrence ; Thread, Runnable, cycle de vie