Java annonce les threads virtuels en Preview version, ils apportent une amélioration dans l’implémentation des threads actuels,
dépannage, débogage et profilage deviennent moins difficiles
Depuis une vingtaine d'années, les développeurs privilégient largement le langage Java pour l’écriture des applications concurrentes. Les threads, plus précisément java.lang.Thread, ont servi d'élément de base. Les threads fonctionnent bien pour représenter une unité d'application concurrente, telle qu'une transaction, car la plateforme et ses outils les connaissent et les suivent. Malheureusement, l'implémentation actuelle des threads consomme un thread OS pour chaque thread Java, et les threads OS sont rares et coûteux, bien plus que les sockets. Les développeurs sont donc obligés de choisir entre un style naturel qui modélise les unités logiques de concurrence directement comme des threads et le gaspillage d'un débit considérable que leur matériel pourrait supporter. Les threads virtuels, implémentations en mode utilisateur de java.lang.Thread, offrent le meilleur des deux mondes.
Rappelons que, plus généralement, un thread est une unité d'exécution faisant partie d'un programme. Cette unité fonctionne de façon autonome et parallèlement à d'autres threads. Le principal avantage des threads est de pouvoir répartir différents traitements d'un même programme en plusieurs unités distinctes pour permettre leurs exécutions « simultanées ». Sur une machine monoprocesseur, c'est le système d'exploitation qui alloue du temps d'utilisation du CPU pour accomplir les traitements de chaque threads, donnant ainsi l'impression que ces traitements sont réalisés en parallèle.
Sur une machine multiprocesseur, le système d'exploitation peut répartir l'exécution sur plusieurs coeurs, ce qui peut effectivement permettre de réaliser des traitements en parallèle. Selon le système d'exploitation et l'implémentation de la JVM, les threads peuvent être gérés de deux manières :
- correspondre à un thread natif du système ;
- correspondre à un thread géré par la machine virtuelle.
Dans les deux cas ci-dessus, cela n'a pas d'impact sur le code qui reste le même. Comme dit précédemment, les threads fonctionnent bien pour représenter une unité d'application concurrente, telle qu'une transaction. La plateforme attache un contexte de dépannage aux exceptions sous la forme de la trace de la pile d'un thread, et les vidages de threads nous permettent d'obtenir un instantané de ce que fait le programme en saisissant les piles de tous les threads ; le débogueur permet de suivre l'exécution d'un thread ; Java Flight Recorder (JFR) émet des événements pour analyse par des profileurs regroupés par thread.
Les développeurs qui écrivent des logiciels serveur à haut débit ont dû utiliser efficacement le matériel pour ne pas le gaspiller, et ont donc dû partager les threads entre les transactions. D'abord en utilisant des pools de threads qui prêtent un thread à une transaction afin d'économiser le coût de la création d'un nouveau thread pour chaque transaction, puis, lorsque cela n'a pas suffi, comme le système d'exploitation ne peut tout simplement pas supporter autant de threads simultanés que nécessaire pour chaque transaction, les développeurs ont commencé à renvoyer les threads au pool même au milieu d'une transaction, lorsqu'elle attend des entrées/sorties. Il en résulte un style de programmation asynchrone, qui non seulement nécessite un ensemble d'API distinct et incompatible, mais rompt également la connexion entre l'unité logique de l'application (transaction) et l'unité de la plateforme (thread), ce qui fait que la plateforme ne connaît pas les unités logiques de l'application.
Par conséquent, le dépannage, l'observation, le débogage et le profilage deviennent très difficiles, car le contexte de la plateforme (le thread) ne représente plus une transaction et n'est donc pas très utile. Un matériel performant est acheté pour le développement avec une maintenance beaucoup plus difficile, ce qui se traduit également par du gaspillage. Étant donné que, l'implémentation actuelle de Thread consomme un thread OS pour chaque thread Java, et que les threads OS sont rares et coûteux, bien plus que, par exemple, les sockets, les serveurs modernes peuvent gérer des ordres de grandeur plus importants de transactions concurrentes que de threads OS. Grâce aux threads virtuels, une implémentation légère des threads en mode utilisateur dont les coûts sont considérablement réduits est possible. Ils permettent aux développeurs de réduire considérablement les efforts d'écriture, de maintenance et d'observation des applications concurrentes à haut débit qui utilisent au mieux le matériel disponible.
Java et les threads virtuels
Les threads virtuels sont aux threads actuels ce que la mémoire virtuelle est à la RAM physique : un mécanisme qui donne l'illusion d'une ressource « virtuelle » abondante grâce à un mappage automatique et efficace vers la ressource « physique » sous-jacente. En utilisant les mêmes API synchrones sur les threads virtuels, ces threads bon marché se bloquent sans bloquer les précieux threads du système d'exploitation. L'utilisation du matériel est proche de l'optimum, ce qui permet un niveau élevé de concurrence et, par conséquent, un débit élevé, tandis que le programme reste en harmonie avec la conception basée sur les threads Java actuels et de ses outils.
Comme les threads virtuels sont bon marché et abondants, les modèles d'utilisation des threads devraient changer. Par exemple, un serveur qui, au cours du traitement d'une demande, consulte deux services distants et attend leurs réponses simultanément, pourrait aujourd'hui soit soumettre deux tâches client HTTP bloquantes à un pool de threads, soit, lancer deux tâches client HTTP asynchrones qui notifient un callback à la fin. Au lieu de cela, il pourrait créer deux threads virtuels, chacun ne faisant rien d'autre que d'exécuter un appel client HTTP au nom de la transaction.
Cette solution serait aussi efficace que l'option asynchrone qui, contrairement à l'option du pool de threads, ne retient pas deux précieux threads du système d'exploitation pendant la durée des requêtes et le code est non seulement aussi familier et simple que l'option du pool de threads, mais aussi plus sûr, car les threads ne sont pas partagés par plusieurs tâches, ce qui risquerait de provoquer une pollution locale des threads. Il n'est pas nécessaire d'apprendre un nouveau modèle de programmation pour utiliser les threads virtuels. Toute personne qui utilise Java pour écrire des applications concurrentes aujourd'hui connaît déjà ce modèle, qui est à peu près le même que le modèle de programmation original de Java.
Pour certains programmeurs, Java serait est un peu coincé avec les threads, et toute solution allant dans le sens des threads donnera lieu à des empreintes mémoires beaucoup plus importants que les solutions allant dans le sens des CPS (continuation-passing style). En programmation fonctionnelle, le style continuation-passing (CPS) est un style de programmation dans lequel le contrôle est passé explicitement sous la forme d'une continuation. Il s'oppose au style direct, qui est le style de programmation habituel.
Une fonction écrite dans le style continuation-passing prend un argument supplémentaire : une « continuation » explicite, c'est-à-dire une fonction à un seul argument. Lorsque la fonction CPS a calculé sa valeur de résultat, elle la « retourne » en appelant la fonction de continuation avec cette valeur comme argument. Les programmes peuvent être automatiquement transformés du style direct au CPS. Les compilateurs fonctionnels et logiques utilisent souvent le CPS comme représentation intermédiaire là où un compilateur pour un langage de programmation impératif ou procédural utiliserait la forme d'affectation unique statique.
Zoom sur les threads virtuels
Les threads virtuels sont des instances de java.lang.Thread implémentées par le JDK de manière à permettre la coexistence d'un grand nombre d'instances actives dans un même processus. Ils peuvent être créés avec l'interface java.lang.Thread.Builder comme suit :
Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable) ;
La méthode Thread::isVirtual permet de savoir si le thread est virtuel ou non. En pratique, comme c'est le cas aujourd'hui, les développeurs construiront rarement directement des threads virtuels à l'aide du constructeur, mais utiliseront plutôt des constructions qui font abstraction de la création des threads, en prenant éventuellement une instance d'une ThreadFactory créée avec le constructeur, comme ceci :
ThreadFactory factory = Thread.ofVirtual().factory() ;
En ce qui concerne le code Java, la sémantique des threads virtuels est identique à celle des threats actuels, sauf qu'ils appartiennent tous à un seul ThreadGroup et ne peuvent pas être énumérés. Cependant, le code natif appelé sur ces threads peut observer un comportement différent ; par exemple, lorsqu'il est appelé plusieurs fois sur le même thread virtuel, il peut observer un ID de thread OS différent à chaque fois. De plus, la surveillance au niveau de l'OS observera que le processus utilise moins de threads d'OS que les threads virtuels créés. Les threads virtuels sont invisibles pour la surveillance au niveau de l'OS, car l'OS n'est pas au courant de leur existence.
Le JDK met en œuvre les threads virtuels en stockant leur état, y compris la pile, sur le tas Java. Les threads virtuels sont planifiés par un planificateur dans les bibliothèques de classes Java, dont les threads travailleurs montent les threads virtuels sur leur dos lorsque les threads virtuels s'exécutent, devenant ainsi leurs porteurs. Lorsqu'un thread virtuel se gare par exemple, lorsqu'il bloque une opération d'E/S ou une construction de synchronisation java.util.concurrent, il est suspendu et le porteur du thread virtuel est libre d'exécuter toute autre tâche.
Lorsqu'un thread virtuel est libéré par exemple, par la fin d'une opération d'E/S. il est soumis à l'ordonnanceur, qui, lorsqu'il est disponible, monte et reprend le thread virtuel sur un thread porteur, pas nécessairement le même que celui sur lequel il s'exécutait précédemment. De cette manière, lorsqu'un thread virtuel effectue une opération bloquante, au lieu de parquer un thread OS, il est suspendu par la JVM et un autre est planifié à sa place, le tout sans bloquer aucun thread OS (voir la section Limitations). Alors que le carrier thread partage son OS thread correspondant avec le virtual thread qu'il monte, du point de vue du code Java, le carrier thread et le virtual thread sont complètement séparés. L'identité du porteur n'est pas connue des threads virtuels, et les traces de pile des deux threads sont indépendantes.
La JVM Tool Interface (JVM TI) peut observer et manipuler les threads virtuels comme les threads de la plateforme, mais certaines opérations ne sont pas prises en charge, comme résumé ci-dessous et détaillé dans la spécification de la JVM TI. En particulier, JVM TI ne peut pas énumérer tous les threads virtuels. De même, l'interface de débogage JDI prend en charge la plupart des opérations sur les threads virtuels, mais ne peut pas les énumérer. JFR associe les événements se produisant sur un thread virtuel au thread virtuel. Les vidages de threads ordinaires montreront tous les threads de la plate-forme en cours d'exécution et les threads virtuels montés, mais un nouveau type de vidage de threads est ajouté, et sera décrit plus tard.
API java.lang.Thread
L'API java.lang.Thread est mise à jour comme suit :
- Thread.Builder, ainsi que Thread.ofVirtual() et Thread.ofPlatform, sont ajoutés en tant que nouvelle API pour créer des threads virtuels et de plateforme Thread.Builder peut également être utilisé pour créer une ThreadFactory ;
- Thread.startVirtualThread(Runnable) est ajouté comme moyen pratique de démarrer un thread virtuel ;
- Thread::isVirtual est ajouté pour tester si un thread est un thread virtuel ;
- Des surcharges de Thread.join et Thread.sleep ont été ajoutées pour permettre de fournir le temps d'attente/de sommeil sous forme de java.time.Duration ;
- Thread.getAllStackTraces() est re-spécifié pour retourner une carte de tous les threads de la plate-forme plutôt que tous les threads.
L'API java.lang.Thread est par ailleurs inchangée. Les constructeurs définis par java.lang.Thread créent des threads de plateforme comme auparavant. Aucun nouveau constructeur public n'a été ajouté. Les différences de l'API entre les threads virtuels et les threads de plateforme sont les suivantes :
- Les constructeurs publics ne peuvent pas être utilisés pour créer des threads virtuels ;
- Les threads virtuels n'ont aucune autorisation lorsqu'ils sont exécutés avec le SecurityManager défini ;
- Les threads virtuels sont des threads démons, la méthode Thread::setDaemon ne peut pas être utilisée pour changer un thread virtuel en un thread non démon ;
- Les threads virtuels ne prennent pas en charge les méthodes stop, suspend ou resume. Ces méthodes sont spécifiées pour lancer une exception si elles sont invoquées sur un thread virtuel ;
- Les threads virtuels ne sont pas des membres actifs d'un groupe de threads. Thread::getThreadGroup renvoie un groupe de threads VirtualThreads qui est vide. L'API Thread.Builder ne peut pas être utilisée pour définir le groupe de threads d'un thread virtuel ;
- Les threads virtuels ont une priorité fixe, Thread.NORM_PRIORITY, qui ne peut être modifiée avec la méthode Thread::setPriority. Cette limitation pourra être revue dans une prochaine version.
Threads locaux
Les threads virtuels prennent en charge les threads locaux et les threads locaux héritables, tout comme les threads de la actuels. Ils peuvent donc exécuter le code existant qui utilise les threads locaux.
En préparation des threads virtuels, de nombreuses utilisations des threads locaux ont été éliminées du module java.base. Cela devrait réduire les problèmes d'empreinte mémoire lors de l'exécution de millions de threads virtuels. L'API Thread.Builder définit une méthode permettant de ne pas utiliser les threads locaux lors de la création d'un thread. Elle définit également une méthode permettant de ne pas hériter de la valeur initiale des thread-locals héritables. Lorsqu'elle est invoquée à partir d'un thread qui ne prend pas en charge les thread-locals, la méthode ThreadLocal::get renvoie la valeur initiale et la méthode ThreadLocal::set lève une exception. Le ClassLoader du contexte hérité est re-spécifié pour fonctionner comme un thread local héritable. Si Thread::setContextClassLoader est invoqué sur un thread qui ne supporte pas les threads locaux, une exception est levée.
API java.util.concurrent
LockSupport, l'API primitive de prise en charge du verrouillage, a été mise à jour pour prendre en charge les threads virtuels. Si un thread virtuel se gare, il libère le thread porteur sous-jacent pour qu'il puisse effectuer d'autres tâches si possible. Le déblocage d'un thread virtuel le soumet au planificateur afin qu'il soit programmé pour continuer. La mise à jour de la prise en charge de LockSupport permet à toutes les API qui l'utilisent (verrous, sémaphores, files d'attente de blocage, ...) de se garer gracieusement lorsqu'elles sont utilisées dans des threads virtuels.
Un petit nombre d'API sont ajoutées
de nouvelles méthodes sont ajoutées à Future pour obtenir le résultat ou l'exception d'une tâche terminée. Il a également été mis à jour avec une nouvelle méthode pour obtenir l'état de la tâche, comme une valeur enum. Combinés, ces ajouts facilitent l'utilisation des objets Future comme éléments de flux (le filtrage peut tester l'état, map peut être utilisé pour obtenir un flux de résultats). Ces méthodes seront également utilisées avec les ajouts d'API proposés pour Structured Concurrency.
Executors.newThreadPerTaskExecutor et Executors.newVirtualThreadPerTaskExecutor sont ajoutés pour retourner un ExecutorService qui crée un nouveau thread pour chaque tâche. Ils peuvent être utilisés pour la migration et l'interopérabilité avec le code existant qui utilise les pools de threads et l'ExecutorService.
ExecutorService est mis à jour pour étendre AutoCloseable, permettant ainsi à cette API d'être utilisée avec la construction try-with-resource.
API de mise en réseau
L'implémentation des API de mise en réseau définies dans les paquets API java.net et java.nio.channels a été mise à jour pour fonctionner avec des threads virtuels. Une opération qui se bloque, par exemple l'établissement d'une connexion réseau ou la lecture d'un socket, libère le thread porteur sous-jacent pour qu'il puisse effectuer d'autres tâches.
Pour permettre l'interruption et l'annulation, les méthodes d'E/S bloquantes définies par java.net.Socket, java.net.ServerSocket et java.net.DatagramSocket ont été respécifiées pour être interruptibles lorsqu'elles sont invoquées dans le contexte d'un thread virtuel. L'interruption d'un thread virtuel bloqué sur une socket entraîne le déblocage du thread et la fermeture de la socket.
API java.io
Le paquet java.io fournit des API pour les flux d'octets et de caractères. Les implémentations de ces API sont fortement synchronisées et nécessitent des modifications pour éviter le pinning lors de l'utilisation de ces API à partir de threads virtuels.
En arrière-plan, les flux d'entrée/sortie orientés octet ne sont pas spécifiés comme étant thread-safe et ne spécifient pas le comportement attendu lorsque close est invoqué alors qu'un thread est bloqué dans une méthode de lecture ou d'écriture. Dans la plupart des scénarios, il n'est pas judicieux d'utiliser un flux d'entrée ou de sortie provenant de threads concurrents. Les lecteurs/écrivains orientés caractères ne sont pas non plus spécifiés pour être thread-safe mais ils exposent un objet lock pour les sous-classes. En dehors de l'épinglage, la synchronisation est problématique et incohérente, par exemple, les encodeurs/décodeurs de flux utilisés par InputStreamReader et OutputStreamWriter se synchronisent sur le flux plutôt que sur l'objet de verrouillage. Comme solution de contournement, pour éviter le pinning, les implémentations sont modifiées comme suit :
- BufferedInputStream,BufferedOutputStream, BufferedReader, BufferedWriter, PrintStream et PrintWriter sont modifiées pour utiliser un verrou explicite plutôt qu'un moniteur lorsqu'elles sont utilisées directement. Ces classes se synchroniseront comme auparavant lorsqu'elles seront sous-classées ;
- les encodeurs/décodeurs de flux utilisés par InputStreamReader et OutputStreamWriter sont modifiés pour utiliser le même verrou que l'InputStreamReader ou l'OutputStreamWriter qui les englobe ;
- PushbackInputStream::close a été modifié pour ne pas conserver de verrou lors de la fermeture du flux d'entrée sous-jacent.
En plus des modifications apportées au verrouillage, la taille initiale des tampons utilisés par BufferedOutptuStream, BufferedWriter, et l'encodeur de flux sous-jacent pour les implémentations de OutputStreamWriter, sont modifiés pour réduire l'utilisation de la mémoire lorsqu'il y a beaucoup de flux de sortie ou d'écrivains dans le tas (comme cela pourrait se produire s'il y a 1M de threads virtuels, chacun avec un flux tamponné sur une connexion socket).
Planificateur
L'ordonnanceur des threads virtuels est un ForkJoinPool qui fonctionne en mode asynchrone (premier arrivé, premier sorti) et dont le parallélisme est fixé au nombre de processeurs disponibles. Certaines API bloquantes bloquent temporairement le thread porteur, par exemple la plupart des opérations d'entrée/sortie de fichiers. Les implémentations de ces API compenseront le blocage en augmentant temporairement le parallélisme au moyen du mécanisme de bloqueur géré ForkJoinPool. En conséquence, le nombre de threads porteurs peut temporairement dépasser le nombre de processeurs disponibles. L'ordonnanceur peut être configuré, à des fins de réglage, à l'aide de deux propriétés système :
- jdk.defaultScheduler.parallelism pour définir le parallélisme, la valeur par défaut étant le nombre de processeurs disponibles ;
- jdk.defaultScheduler.maxPoolSize pour limiter le nombre de threads porteurs lorsque le parallélisme est étendu. La valeur par défaut est de 256.
Interface native Java (JNI)
La JNI a été mise à jour pour définir une nouvelle fonction, IsVirtualThread, afin de tester si un objet est un Thread virtuel. Pour le reste, la spécification JNI est inchangée.
Débogueur
L'architecture du débogueur se compose de trois interfaces, à savoir la JVM Tool Interface (JVM TI), le Java Debug Wire Protocol (JDWP) et la Java Debug Interface (JDI). Ces trois interfaces ont été mises à jour pour prendre en charge les threads virtuels. La JVM TI a été considérablement mise à jour comme suit :
- la plupart des fonctions qui sont appelées avec un jthread (une référence JNI à un objet Thread) peuvent être appelées avec une référence à un objet Thread pour un thread virtuel. Un petit nombre de fonctions, à savoir PopFrame, ForceEarlyReturn, StopThread, AgentStartFunction et GetThreadCpuTime, ne sont pas prises en charge sur les threads virtuels. La fonction SetLocalXXX n'est prise en charge par les threads virtuels que dans des cas limités ;
- les fonctions GetAllThreads et GetAllStackTraces ont été respécifiées pour renvoyer tous les threads de la plate-forme plutôt que tous les threads ;
- Tous les événements, à l'exception de ceux affichés pendant le démarrage précoce de la VM ou pendant l'itération du tas, peuvent avoir des rappels d'événements invoqués dans le contexte d'un thread virtuel ;
- une nouvelle capacité can_support_virtual_threads est ajoutée pour les agents qui sont développés, ou mis à jour, pour supporter les threads virtuels. Cette capacité permet aux agents d'avoir un contrôle plus fin sur les événements de début et de fin de thread pour les threads virtuels ;
- l'implémentation de la suspension/reprise a été modifiée de manière significative afin que les threads virtuels puissent être suspendus et repris par les débogueurs. elle permet également aux threads porteurs d'être suspendus lorsqu'un thread virtuel est monté ;
- De nouvelles fonctions ont été ajoutées pour prendre en charge la suspension et la reprise en masse des threads virtuels. Ces nouvelles fonctions nécessitent la capacité can_support_virtual_threads.
Les agents JVM TI existants fonctionneront pour la plupart comme avant, mais pourront rencontrer des erreurs s'ils invoquent des fonctions qui ne sont pas prises en charge par les threads virtuels. Cela se produira lorsqu'un agent « ignorant les threads virtuels » est utilisé avec une application qui utilise des threads virtuels. La modification de GetAllThreads, qui renvoie un tableau contenant uniquement les threads de la plateforme, peut également poser un problème pour certains agents. Il peut également y avoir des problèmes de performance pour les agents existants qui activent les événements ThreadStart/ThreadEnd car ils n'ont pas la possibilité de limiter les événements aux seuls threads de la plateforme. JDWP est mis à jour comme suit :
- une nouvelle commande est ajoutée au protocole pour permettre aux débogueurs de tester si un thread est un thread virtuel ;
- un nouveau modificateur est ajouté à la commande EventRequest pour permettre aux débogueurs de limiter les événements de début/fin de thread aux threads de la plateforme.
JDI est mis à jour comme suit :
une nouvelle méthode est ajoutée à com.sun.jdi.ThreadReference pour tester si un thread est un thread virtuel.
Une nouvelle méthode est ajoutée à com.sun.jdi.request.ThreadStartRequest et com.sun.jdi.request.ThreadDeathRequest pour restreindre l'événement généré pour la requête aux seuls threads de la plateforme.
Comme indiqué ci-dessus, les threads virtuels ne sont pas considérés comme des threads actifs dans un groupe de threads. Par conséquent, la fonction JVM TI GetThreadGroupChildren, la commande JDWP ThreadGroupReference/Children et la méthode JDI com.sun.jdi.ThreadGroupReference::threads renvoient une liste des threads de la plate-forme dans le groupe de threads, mais pas une liste des threads virtuels.
Java Flight Recorder (JFR)
JFR est mis à jour pour supporter les threads virtuels. Un certain nombre de nouveaux événements sont ajoutés : jdk.VirtualThreadStart et jdk.VirtualThreadEnd pour le début et la fin des threads virtuels. Ces événements sont désactivés par défaut. jdk.VirtualThreadPinned pour indiquer qu'un thread virtuel s'est parker alors qu'il était épinglé. Cet événement est activé par défaut avec un seuil de 20ms. jdk.VirtualThreadSubmitFailed pour indiquer que le démarrage ou le dé-parking d'un thread virtuel a échoué, probablement en raison d'un problème de ressources. Cet événement est activé par défaut.
Limites des threads virtuels
Il existe des situations où la VM ne peut pas suspendre un thread virtuel, auquel cas on dit qu'il est épinglé. Actuellement, il en existe deux :
- lorsqu'une méthode native est en cours d'exécution dans le thread virtuel (même s'il s'agit d'un appel vers Java) ;
- lorsqu'un moniteur natif est retenu par le thread virtuel, ce qui signifie qu'il est en train de s'exécuter à l'intérieur d'un bloc ou d'une méthode synchronisée.
La première limitation est là pour rester, tandis que la seconde pourrait être supprimée à l'avenir. Lorsqu'un thread virtuel tente de se parker, par exemple en effectuant une opération d'E/S bloquante, alors qu'il est épinglé, plutôt que libéré, son thread OS sous-jacent sera bloqué pendant la durée de l'opération. Pour cette raison, un blocage très fréquent pendant de longues durées peut nuire à l'évolutivité des threads virtuels.
Par conséquent, pour tirer le meilleur parti des threads virtuels, les blocs ou méthodes synchronisés qui sont exécutés fréquemment et qui gardent des opérations d'E/S potentiellement longues doivent être remplacés par java.util.concurrent.ReentrantLock. Il n'est pas nécessaire de remplacer les blocs et méthodes synchronisés qui ne sont pas fréquents (par exemple, qui ne sont exécutés qu'au démarrage) ou qui protègent les opérations en mémoire, bien qu'il soit toujours bon d'envisager java.util.concurrent.StampedLock dans ce dernier cas. Comme toujours, la priorité est de conserver une politique de verrouillage simple et claire. Pour faciliter la migration et aider à évaluer si une utilisation particulière de synchronized doit être envisagée pour être remplacée par un verrou j.u.c, l'événement JFR jdk.VirtualThreadPinned sera émis lorsqu'un thread virtuel tente de se garer alors qu'il est épinglé (avec un seuil par défaut de 20ms).
Source : Java
Et vous ?
Que pensez-vous de la gestion des threads en Java ?
Quel est votre avis sur les threads virtuels en Java ?
Croyez-vous que l'approche CPS peut apporter une différence par rapport aux threads virtuels ?
Voir aussi :
Microsoft s'investit dans Java et rejoint le Java Community Process, les services back-end de LinkedIn, et de Yammer sont presque entièrement mis en œuvre sur des microservices Java
À quel point Java 17, la dernière version du langage, est-il plus rapide ? Voici une comparaison avec Java 11 et Java 16
Développeurs Java : la version d'OpenJDK de Microsoft est désormais disponible et inclut des binaires pour Java 11, basés sur OpenJDK 11.0.11
JVM Ecosystem Report 2021 de Snyk : 61,5 % des développeurs utilisent Java 11 et près de 12 % utilisent la dernière version, Java 15, Java 8 est toujours sollicité dans la pile de production
Partager