Fermer

mars 21, 2023

Java Project Loom – Lancement de 10 millions de threads (Partie 2)

Java Project Loom – Lancement de 10 millions de threads (Partie 2)


Dans le blog précédent, nous avons discuté d’un aperçu détaillé du projet Loom. Il est maintenant temps pour un peu de code. Si vous n’avez pas lu la partie 2 de cette série, veuillez la consulter ici :

Voyons comment nous pouvons créer des threads virtuels en Java.

Thread.ofVirtual().unstarted(() -> System.out.println("Thread is not started"));
 Thread.ofVirtual().start(() -> System.out.println("Thread is not started"));

Dans le premier exemple, nous avons créé un thread virtuel mais en mode non démarré. Dans le deuxième exemple, nous avons créé un thread virtuel en mode démarré. Des trucs simples. Il n’y a rien d’extraordinaire à ce sujet.

Exécutons maintenant le programme ci-dessous et voyons le résultat

public static void main(String[] args) throws InterruptedException {
        var thread = Thread.ofVirtual().unstarted(() -> System.out.println(Thread.currentThread()));
        thread.start();
        thread.join();
    }

Sortir

VirtualThread[

We can see that VirtualThread [#22] a été créé et exécute la tâche d’impression de la ligne. Aussi, on peut voir ForkJoinPool-1-travailleur-1 jeiteral dans la sortie. Cela signifie que notre VirtualThread[#22] s’exécute au-dessus d’un thread de plate-forme portant le nom ForkJoinPool-1-travailleur-1. De toute évidence, les threads virtuels s’exécutent au-dessus du ForkJoinPool. Est-ce le même ForkJoinPool commun que nous utilisons pour soumettre la tâche ? Vérifions cela en exécutant le programme suivant :

public static void main(String[] args) throws InterruptedException {
    ForkJoinPool.commonPool().submit(
          () -> System.out.println(Thread.currentThread())
        );
}

Sortir

Thread[

Clairement, ForkJoinPool-1-travailleur-1 et ForkJoinPool.commonPool-worker-1 ne sont pas les mêmes. Les threads virtuels s’exécutent sur le pool de jointure fork qui est différent du pool de jointure fork commun introduit dans Java 7.

Voyons une autre chose intéressante à propos du thread virtuel en exécutant le programme suivant

 public static void main(String[] args) throws InterruptedException {
    Thread.ofVirtual().start(() -> System.out.println("Thread is not started"));
    var threads = IntStream.range(0,10)
      .mapToObj(
          index -> Thread.ofVirtual().unstarted(() -> {
              if(index == 0){
                  System.out.println(Thread.currentThread());
              }
              try {
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              if(index == 0){
                  System.out.println(Thread.currentThread());
              }
          })).toList();
    threads.forEach(Thread::start);
    for(Thread thread : threads){
        thread.join();
    }
  }

Nous créons 10 threads virtuels et laissons le 0ème thread imprimer son nom de thread. Nous lui permettons de dormir pendant 10 millisecondes, puis nous essayons à nouveau d’imprimer le 0ème thread pour imprimer son nom. Voyons le résultat.

VirtualThread[
VirtualThread[

On peut voir ça Fil virtuel #25 exécuté pour la première fois sur le fil de la plate-forme ForkJoinPool-1-travailleur-2. Il est endormi depuis 10 millisecondes. Lorsqu’il se réveille, il est épinglé à un autre thread de plate-forme avec le nom exécutable@ForkJoinPool-1-worker-10. Donc clairement, nous pouvons voir que chaque fois que le thread virtuel est bloqué, il libère le thread de la plate-forme et sa pile est déplacée vers la mémoire de tas. Le thread de la plate-forme peut maintenant être épinglé à un autre thread virtuel pour effectuer sa tâche. Lorsque l’opération de blocage est terminée, le thread virtuel sera épinglé à n’importe quel thread de plate-forme libre et montera sa pile sur ce thread de plate-forme.

C’est nouveau en Java, et c’est génial. Bloquer un fil n’est pas cher. Le coût consiste uniquement à démonter la pile d’appels du thread de la plate-forme et à la stocker dans le tas et vice versa.

Il est maintenant temps de lancer les discussions. Lançons 100 threads avec le programme ci-dessous.

 public static void main(String[] args) throws InterruptedException {
     var list = new HashSet<String>();
     var millis = System.currentTimeMillis();
     var vthreadcounts = 100;
     var threads = IntStream.range(0,vthreadcounts)
        .mapToObj(
          index -> Thread.ofVirtual().unstarted(() -> {
              var abc =  "VirtualThread[#22]/runnable@ForkJoinPool-1-worker-1".contains("runnable@ForkJoinPool-1");
              var threadName = Thread.currentThread().toString();
             if(abc) list.add(threadName.substring(threadName.indexOf("https://www.tothenew.com/")));
          })).toList();

      threads.forEach(Thread::start);
      for(Thread thread : threads){
          thread.join();
      }
      var millis1 = System.currentTimeMillis();
      System.out.println("millis used to launch " + vthreadcounts + "vthreads:" + (millis1 - millis) + "ms");
      System.out.println("number of platform thread used:" + list.size());
   }

Nous utilisons une certaine logique pour calculer le nombre de threads de plate-forme requis pour exécuter 100 threads virtuels. De plus, nous avons écrit une logique pour calculer le temps qu’il faut en millisecondes pour l’exécuter. Voyons la sortie de ce programme.

millis used to launch 100vthreads:23ms
number of platform thread used:7

Cela a pris 23 millisecondes et a utilisé 7 threads de plate-forme.

Mettons plus de pression sur le programme en changeant les vthreadcounts à 1000. Voyons maintenant la sortie.

millis used to launch 1000vthreads:23ms
number of platform thread used:10

Rien, cela a quand même pris 23 millisecondes et le thread de plate-forme utilisé est de 10 dans ce cas. Mettons plus de pression dessus et exécutons 10 000 threads et voyons le résultat.

millis used to launch 10000vthreads:57ms
number of platform thread used:10

Cela a pris maintenant 57 millisecondes et le thread de plate-forme utilisé est de 10. Assez impressionnant. Mettons un peu plus de pression dessus et exécutons 100 000 threads et voyons le résultat.

millis used to launch 100000vthreads:246ms
number of platform thread used:10

Toujours seulement 246 millisecondes et 10 threads de plate-forme. Toujours sous 1 seconde. Il est temps de mettre une pression sérieuse et de rechercher 1000 000 (1 million) fils.

millis used to launch 1000000vthreads:1286ms
number of platform thread used:10

Maintenant, cela prenait 1286 millisecondes, juste un peu plus d’une seconde et utilisait 10 threads de plate-forme.

Maintenant, nous sommes en finale et courrons pour 10 millions de threads, c’est-à-dire 10 000 000 de threads. As-tu déjà fait ça. C’est une quantité folle de chiffres.

millis used to launch 10000000vthreads:8762ms
number of platform thread used:10

Et nous sommes capables d’exécuter nos 10 millions de threads en un peu moins de 9 secondes.

Même si nous ne faisons pas grand-chose dans nos threads, mais exécuter 10 millions de threads en moins de 9 secondes n’est pas une mince affaire.

Les gars de Project Loom font un excellent travail. Dans les parties à venir, nous examinerons StructuredTaskScope, une fonctionnalité assez étonnante de ce projet Loom.

Ce blog a été initialement publié dans Medium, lisez ici.

TROUVÉ CELA UTILE ? PARTAGEZ-LE




Source link