Thread, Runnable, join

Hello Thread

On souhaite créer 4 threads (le nombre peut changer) qui exécutent un même code affichant les nombres de 0 à 5 000.
Histoire de différencier les threads à l'affichage, chaque thread affichera en plus du nombre courant un numéro (0 pour le premier thread, 1 pour la second, etc).
Par exemple, on pourra obtenir ce type d'affichage :

        ...
        hello 0 1714
        hello 0 1715
        hello 0 1716
        hello 0 1717
        hello 1 1096
        hello 1 1097
        hello 1 1098
        hello 1 1099
        ...
      
Rappel : créer un Runnable se fait en utilisant la syntaxe des lambdas, comme ceci
        Runnable runnable = () -> {
        ...
        };
      
Attention : on vous demande de faire tout l'exercice sans utiliser les méthodes name ou setName.

Rappeler à quoi sert un Runnable.

Écrire, dans un premier temps, une classe HelloThread qui crée et démarre 4 threads (et faire en sorte qu'on puisse facilement en demander 150) qui affichent les nombres de 0 à 5 000 (sans le numéro unique par thread, donc).

Exécutez le programme plusieurs fois, que remarque-t-on ? Puis, en regardant l'affichage (scroller au besoin), qu'y a-t-il de bizarre ? Est-ce que tout ceci est bien normal ?

Modifiez votre code pour afficher en plus le numéro de chaque thread (sans utiliser le numéro du thread, juste la variable de boucle).

Rappel : À l'intérieur d'une lambda, il n'est possible d'utiliser des variables déclarées à l'extérieur que si leur valeur ne change pas.

This is the end, my friend ...

On souhaite afficher le message "le programme est fini", lorsque tous les threads ont fini de faire leurs calculs.

Recopiez le programme de l'exercice précédent dans une nouvelle classe HelloThreadJoin puis modifiez le pour que soit affiché le message "le programme est fini" lorsque tous les threads ont fini leurs calculs.

Si vous cherchez comment attendre que des threads aient fini d'exécuter leur Runnable, la méthode que vous cherchez est la méthode d'instance join.
          Runnable runnable = () -> {
            ...
          };
          var thread = Thread.ofPlatform().start(runnable);
          thread.join();
          System.out.println("Le thread a fini son Runnable");
        
Attention : les threads doivent continuer à s'entrelacer !

JConsole

On veut réaliser une classe TurtleRace dont un squellette du main est donné ci-dessous :

public static void main(String[] args) throws InterruptedException {
  System.out.println("On your mark!");
  Thread.sleep(30_000);
  System.out.println("Go!");
  int[] times = {25_000, 10_000, 20_000, 5_000, 50_000, 60_000};
  ...
}

Le main attend 30 000 millisecondes (i.e., 30 secondes) puis devra lancer autant de threads qu'il y a de valeurs dans le tableau times. Le premier thread s'appelera Turtle0 est attendra times[0] (c'est à dire 25 000 millisecondes) avant d'afficher Turtle 0 has finished. Le second thread attendra times[1] millesecondes avant d'afficher Turtle 1 has finished. Et ainsi de suite ...

Réaliser la classe TurtleRace. Bien entendu, votre code doit fonctionner si l'on modifie le tableau times.

Vous devriez avoir comme un affichage qui ressemble à ça :

On your mark!
Go!
Turtle 3 has finished
Turtle 1 has finished
Turtle 2 has finished
Turtle 0 has finished
Turtle 4 has finished
Turtle 5 has finished
Attention : le temps d’exécution la méthode sleep est dépendant du système. Elle ne peut en aucun cas servir à garantir l'ordre d’exécution des threads.

Juste après avoir exécuté la classe TurtleRace, lancez jconsole dans un terminal.

$ /usr/local/apps/java11/bin/jconsole

Il faut se connecter sur la classe TurtleRace et accepter le warning sur la connexion non-sécurisée. Vous devriez avoir ce genre d'affichage :


Observer l'évolution du nombre de threads. Que devient le thread main ? Quand est-ce que la JVM s'éteint ?

La magie de System.out.println

La classe HelloThreadBis est une solution à la première question de l'exercice 1. La seule différence notable avec votre solution devrait être qu'au lieu d'utiliser System.out.println, nous avons recodé une fonction qui réalise l'affichage de la chaîne, caractère par caractère.

public static void println(String s){
  for(var i = 0; i < s.length(); i++){
    System.out.print(s.charAt(i));
  }
  System.out.print("\n");
}
Exécuter la classe HelloThreadBis et comparer la sortie avec ce que vous avez obtenu avec la classe HelloThread de l'exercice 1.
Vous devriez voir dans la sortie de la classe HelloThreadBis des lignes comme celle-ci :
hello 4725
hello 4726
hellhello 4889
hello 4890
hello 4891
hello 4892
hello 4893
hello 4894
hello 4895
hello 4896
hello 4897
hello 4898
hello 4899
hello 4900
hello 4901
hello 4902
hello 4903
o 4hello 4904
hello 4905
Expliquer le comportement observé.
Pourquoi ce comportement n’apparaît-il pas quand on utilise System.out.println ?
Vous pouvez regarder le code la méthode System.out.println