Les coroutines en Java : Loom et les Virtual Threads

Pendant longtemps, Java a utilisé un modèle de thread lourd, reposant directement sur les threads du système d’exploitation. Chaque Thread Java était en réalité une enveloppe autour d’un thread natif (souvent POSIX ou Windows), ce qui limitait la scalabilité : il devenait coûteux en ressources de gérer un grand nombre de threads, surtout dans les applications très concurrentes comme les serveurs web ou les systèmes de traitement d’événements en temps réel.

Avec Project Loom, Java introduit une innovation majeure : les virtual threads, une implémentation de coroutines légères directement intégrée à la JVM. Cette avancée permet de libérer les développeurs des contraintes du modèle asynchrone traditionnel, basé sur les callbacks, les futures ou les flux réactifs, souvent complexes à manipuler et à maintenir.

Cet article explore les fondements des coroutines en Java à travers Loom, leurs avantages, leurs limites, et leur usage pratique dans des cas concrets.

1. Comprendre les coroutines et les virtual threads

1.1 Qu’est-ce qu’une coroutine ?

Une coroutine est une unité d’exécution légère, pouvant être suspendue et reprise à volonté sans bloquer le thread natif sous-jacent. Cela permet d’exécuter plusieurs tâches de manière concurrente, tout en gardant une écriture synchrone et naturelle du code.

Dans des langages modernes comme Kotlin, Python ou Go, les coroutines sont devenues un standard pour écrire des applications hautement concurrentes sans sacrifier la lisibilité du code.

Java, historiquement attaché au modèle de thread bloquant, adopte aujourd’hui ce paradigme grâce à Loom.

1.2 Les virtual threads dans Java

Les virtual threads sont des threads gérés par la JVM, détachés des threads système natifs. Ils sont planifiés par un scheduler coopératif intégré à la JVM, ce qui les rend extrêmement légers. Leur création, suspension et gestion ne nécessitent pas l’intervention du noyau de l’OS.

Caractéristiques clés :

  • Très légers : des millions peuvent coexister dans une seule JVM.
  • Planification coopérative : la JVM gère le yield/suspend/reprise de manière intelligente.
  • Interopérables : ils utilisent toujours l’API standard java.lang.Thread.
  • Non bloquants : les appels bloquants comme Thread.sleep() ou Socket.read() sont gérés intelligemment pour ne pas bloquer de thread natif.

2.  Utilisation des virtual threads

2.1 Création d’un virtual thread

Depuis Java 21 (ou Java 19 via les fonctionnalités preview), la création d’un virtual thread est extrêmement simple :

Thread.startVirtualThread(() -> {

    System.out.println(“Hello depuis un virtual thread !”);

});

Ou en utilisant un ExecutorService dédié :

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

executor.submit(() -> {

    Thread.sleep(1000);

    System.out.println(“Tâche exécutée !”);

    return null;

});

2.2 Création d’un virtual thread

3. Avantages des virtual threads

  • Écriture synchrone, exécution concurrente : fini les chaînes de CompletableFuture.thenCompose(…).
  • Scalabilité massive : adapté aux serveurs manipulant des dizaines de milliers de connexions simultanées.
  • Interopérabilité totale : aucune modification majeure du code existant.
  •  Facilité de débogage : le stack trace reste lisible, contrairement aux flux réactifs.
  • Meilleure gestion des ressources : les virtual threads sont suspendus lors des appels bloquants, sans monopoliser les threads natifs.

4. Cas d’usage typiques

4.1 Serveurs HTTP

Les frameworks comme Spring Boot (depuis 6.1), Helidon, ou Tomcat expérimentent une exécution “thread-per-request” via virtual threads, permettant une scalabilité horizontale sans l’usage de frameworks réactifs complexes comme WebFlux.

4.2 Traitement parallèle massif

Dans les tâches I/O-bound comme :

  • Lancement parallèle de requêtes HTTP
  • Traitement de fichiers
  • Traitement de batchs de données

Les virtual threads permettent de paralléliser sans coût significatif.

4.3 Simplification des APIs asynchrones

Des APIs comme CompletableFuture, RxJava ou Project Reactor peuvent être remplacées par des boucles for ou try/catch, offrant lisibilité et débogabilité accrues.

5. Bonnes pratiques et limites

Bonnes pratiques :

  • Utiliser un ExecutorService par tâche ou par domaine (isolation).
  • Éviter le synchronized : préférez ReentrantLock ou les outils java.util.concurrent.
  • Surveiller la consommation mémoire (heap) : bien que légers, des millions de threads consomment quand même de la stack (réduite à 256 Ko par défaut).
  • Profiler avec JFR, VisualVM, ou jstack.

Limites actuelles :

  • Pas optimal pour les workloads CPU-bound (les threads concurrents se disputent les cœurs).
  • Les appels JNI bloquants (accès à du code natif) peuvent figer des threads de portage.
  • Certaines bibliothèques non coopératives (anciennes libs IO ou DB) ne tirent pas profit des virtual threads.

6. Perspectives et avenir

Project Loom ouvre la voie à un Java plus ergonomique, qui pourrait, à terme :

  • Remplacer progressivement les paradigmes réactifs complexes.
  • Favoriser l’écriture de middlewares plus simples (serveurs RPC, gestionnaires de tâches, etc.).
  • Uniformiser la programmation concurrente dans l’ensemble de l’écosystème Java.

Il reste encore des efforts à faire, notamment pour adapter des bibliothèques comme JDBC, ou les connecteurs natifs, mais la direction est claire : un Java moderne, scalable, et agréable à utiliser.

Conclusion

Project Loom et les virtual threads apportent une véritable révolution dans la gestion de la concurrence en Java. En rendant le modèle concurrent simple, performant et scalable, ils changent la façon de concevoir les applications modernes.

Les développeurs peuvent désormais écrire du code synchrone et lisible, tout en bénéficiant des performances du modèle asynchrone, sans devoir plonger dans les complexités du réactif ou du multi-threading bas niveau.

Avec Loom, Java rejoint la modernité concurrente, en s’inspirant des meilleurs concepts des autres langages tout en restant fidèle à ses fondations solides.

Nos autres articles

Partager cet article:

Nous Contacter

Une question, une candidature, une offre ?  Écrivez-nous…

01 84 20 94 37

contact@sijo.fr

43 Rue Pierre Brossolette, 92300