:: Enseignements :: Master :: M1 :: 2016-2017 :: Java Avancé ::
[LOGO]

Thread, Runnable, join, synchronized, interrupt


Exercice 1 - 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 la première thread, 1 pour la seconde, 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 r = () -> {
        ...
        };
      

  1. Rappeler à quoi sert un Runnable ?
  2. Ecrire dans un premier temps, une classe HelloThread qui crée et démarre 4 threads qui affichent les nombres de 0 à 5 000 (sans numéro unique par thread donc).
  3. 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 ?
  4. Modifiez votre code pour afficher en plus le numéro de chaque thread.
    Rappel de Java:
    1. Il est possible d'utiliser à l'intérieur d'une lambda des variables déclarées à l'extérieur que si leur valeur ne change pas.

Exercice 2 - This is the end, my friend ...

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

  1. 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 toutesles 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 Thread.join.
              Runnable r = () -> {
              ...
              };
              Thread t = new Thread(r);
              t.start();
              t.join();
              System.out.println("La thread t a fini son Runnable");
            

Exercice 3 - When things add up

On souhaite modifier le programme précédent pour qu'au lieu d'afficher les nombres, on les stocke dans une unique ArrayList (une seule liste pour toutes les threads) dont on affichera la taille à la fin du programme.

  1. Recopiez la classe HelloThreadJoin dans une nouvelle classe HelloListBug puis modifiez la pour ajouter les nombres au lieu de les afficher et pour afficher la taille finale une fois toutes les threads terminées.
    Exécuter le programme plusieurs fois et noter les différents affichages (oui, même les exceptions).
  2. Expliquer quel est le problème lorsqu'une exception est levée. Pour comprendre, il faut regarder le code de la méthode ArrayList.add.
  3. Puisque l'exception se produit lorsque l'on agrandit l'ArrayList, on peut essayer de la créer avec la bonne taille.
              ArrayList list = new ArrayList(5_000*4);
            

    Exécuter le programme plusieurs fois et noter les différents affichages.
    Expliquer quel est le problème.
  4. Corriger le problème et vérifier que la correction que vous avez effectuée, exécute bien les threads en parallèle et non pas les unes derrière les autres.

Exercice 4 - Coitus interruptus

On souhaite avoir 4 threads qui affichent chacune leur numéro et un compteur indéfiniment (chaque thread a son propre compteur). Pour éviter de faire chauffer la machine, l'affichage se fera une fois par seconde (en utilisant Thread.sleep()).
De plus, la thread main va lire des entiers sur l'entrée standard et si l'utilisateur entre une valeur correspondant au numéro d'une thread, cette dernière sera arrêtée.
Le code pour lire sur l'entrée standard est le suivant:
        System.out.println("enter a thread id:");
        try(Scanner scanner = new Scanner(System.in)) {
          while(scanner.hasNextInt()) {
            int threadId = scanner.nextInt();
            ...
          }
        }
      
Note: dans les vrais programmes, on utilise rarement le Scanner car il est très lent (comme le scanf en C), on utilise plutôt un BufferedReader ou Files.lines.
Rappel: on utilise Ctrl-D (Ctrl-Z sous Microsoft Windows) pour indiquer au terminal qu'il faut fermer l'entrée standard.

  1. Pourquoi n'est il pas possible d'arréter une thread de façon non coopérative ?
  2. Expliquer comment utiliser les méthodes thread.interrupt et Thread.interrupted (noter que la seconde est statique et pas la première) pour arrêter des threads dans le cas où il n'y a pas d'opération bloquante.
  3. Et si il y a une opération bloquante ?
  4. Que se passe-t-il lorsque l'on appelle Thread.currentThread().interrupt() ?
  5. Expliquer la trop subtile différence entre les méthodes Thread.interrupted et thread.isInterrupted de la classe Thread.
    Pourquoi dit-on que la méthode Thread.interrupted est mal nommée ?
  6. Ecrire le code.
  7. Comment faire pour que le programme se termine si l'on fait un Ctrl-D dans le terminal ?