JDK19 : Threads virtuels et améliorations majeures pour la programmation concurrente Java

Publié le 20 septembre 2022, JDK19 est une version non LTS qui introduit plusieurs fonctionnalités révolutionnaires en phase de prévisualisation ou d'incubation. Les threads virtuels, en particulier, révolutionnent la programmation concurrente Java en offrant une alternative légère aux threads traditionnels. Cette itération comprend sept JEP (Java Enhancement Proposals) couvrant la concurrence, la syntaxe, les API natives et l'adaptation multiplateforme.

Les fonctionnalités incluses sont :

  • JEP 425 : Threads virtuels (prévisualisation) – une avancée majeure pour la concurrence Java
  • JEP 428 : Concurrence structurée (incubation) – gestion améliorée des tâches multithread
  • JEP 405 : Motifs d'enregistrement (prévisualisation) – déconstruction simplifiée des records
  • JEP 427 : Correspondance de motifs dans switch (troisième prévisualisation) – syntaxe épurée
  • JEP 424 : API pour fonctions et mémoire externes (prévisualisation) – alternative à JNI
  • JEP 426 : API Vectorielle (quatrième incubation) – optimisation des calculs numériques
  • JEP 422 : Portage Linux/RISC-V – support étendu des plateformes

Innovations dans la concurrence Java

Les threads virtuels (JEP 425) sont des threads légers gérés par la JVM plutôt que par le système d'exploitation. Ils éliminent les limitations des threads traditionnels, tels que les coûts élevés de création et de basculement de contexte. Avec une empreinte mémoire réduite à quelques octets, ils permettent la création de millions de threads, idéal pour les applications à intensité d'E/S comme les services web ou les opérations de base de données.

Exemple de code pour démarrer un thread virtuel :


public class DemoFilsVirtuels {
    public static void main(String[] args) {
        // Lancer un fil virtuel directement
        Thread.startVirtualThread(() -> {
            System.out.println("Tâche exécutée dans le fil : " + Thread.currentThread().getName());
        });

        // Utiliser un exécuteur avec des fils virtuels par tâche
        try (var service = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int compteur = 0; compteur < 10000; compteur++) {
                service.submit(() -> {
                    Thread.sleep(100); // Simulation d'une opération bloquante
                    return Thread.currentThread().getName();
                });
            }
        }
    }
}

La concurrence structurée (JEP 428) offre un modèle pour gérer le cycle de vie des tâches concurrentes. Elle utilise StructuredTaskScope pour regrouper les sous-tâches en une unité de travail unique, facilitant la gestion des erreurs et l'annulation coordonnée.


public class DemoConcurrenceStructuree {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Supplier<String> tacheUtilisateur = scope.fork(() -> recupererInfosUtilisateur());
            Supplier<String> tacheCommande = scope.fork(() -> recupererInfosCommande());

            scope.join();
            scope.throwIfFailed();

            System.out.println("Utilisateur : " + tacheUtilisateur.get());
            System.out.println("Commande : " + tacheCommande.get());
        }
    }

    private static String recupererInfosUtilisateur() throws InterruptedException {
        Thread.sleep(50);
        return "Développeur Java";
    }

    private static String recupererInfosCommande() throws InterruptedException {
        Thread.sleep(50);
        return "Tutoriel JDK19";
    }
}

Simplifications syntaxiques

Les motifs d'enregistrement (JEP 405) permettent de déstructurer les records directemetn dans une condition instanceof, éliminant les conversions et accès aux propriétés manuels.


record Utilisateur(String nom, int age) {}
record Commande(Utilisateur utilisateur, String numeroCommande) {}

public class DemoMotifsEnregistrement {
    public static void main(String[] args) {
        Object objet = new Commande(new Utilisateur("Développeur Java", 25), "CMD_001");

        // Déconstruction directe avec des motifs
        if (objet instanceof Commande(Utilisateur(var nom, var age), var numero)) {
            System.out.println("Nom : " + nom + ", Âge : " + age + ", Commande : " + numero);
        }
    }
}

La correspondance de motifs dans switch (JEP 427) améliore la gestion des branches avec support des valeurs nulles et des gardes de motif, réduisant les risques d'erreurs.


public class DemoMotifsSwitch {
    static String formaterObjet(Object obj) {
        return switch (obj) {
            case null -> "Objet nul";
            case String s when s.length() > 10 -> "Longue chaîne : " + s.toUpperCase();
            case String s -> "Courte chaîne : " + s;
            case Integer i -> "Nombre : " + (i * 2);
            default -> "Autre type : " + obj;
        };
    }

    public static void main(String[] args) {
        System.out.println(formaterObjet("Tutoriel JDK19"));
        System.out.println(formaterObjet(10));
        System.out.println(formaterObjet(null));
    }
}

API natives et optimisations

L'API pour fonctions et mémoire externes (JEP 424) remplace JNI pour un accès sécurisé aux bibliothèques locales et à la mémoire hors tas, avec une meilleure performance et simplicité. L'API Vectorielle (JEP 426) exploite les registres SIMD pour accélérer les calculs numériques, utile en science des données ou traitement d'images.

Adaptation multiplateforme

Le portage Linux/RISC-V (JEP 422) étend le support de Java aux architectures matérielles émergentes, facilitant les déploiements sur des plateformes spécialisées.

Pour utiliser ces fonctionnalités en prévisualisation, ajoutez le paramètre JVM --enable-preview lors de la compilation et de l'exécution. Évitez d'utiliser synchronized ou ThreadLocal dans les threads virtuels pour ne pas compromettre leur légèreté ; préférez les verrous explicites comme ReentrantLock.

Étiquettes: JDK19 Virtual Threads Structured Concurrency Record Patterns Switch Pattern Matching

Publié le 23 juin à 18h11