:: Enseignements :: Master :: M1 :: 2014-2015 :: Java Avancé ::
[LOGO]

Thread, Runnable, join, synchronized, interrupt


Exercice 1 - Hello Thread

On souhaite créer 4 threads (le nombre peu changer) qui exécute un même code afffichant les nombre 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 affiche les nombres de 0 à 5000 (sans numéro unique par thread donc).
  3. Exécuter 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 tous ceci est bien normal ?
  4. Modifier votre code pour afficher en plus le numéro de chaque thread.
    Rappel de Java:
    1. Il n'est possible d'utiliser à l'intérieur d'une lambda des variables déclarées à l'extérieur que si la valeur de la variable ne change pas.

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

On souhaite afficher 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 sous un nouveau nom de 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 on fait pour attendre que des threads aient fini d'exécuter leurs 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 stocke ceux-ci 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 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 vous faut regarder le code de la méthode ArrayList.add.
  3. Puisque l'exception se produit lorsque l'on agrandit la ArrayList, on peut essayer de créer la ArrayList 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, celle-ci va arrêter la thread correspondante.
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 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 appel Thread.currentThread().interrupt() ?
  5. Expliquer la trop subtile différences 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 ?