:: Enseignements :: ESIPE :: E4INFO :: 2013-2014 :: Java Réseau I - Concurrence et E/S ::
[LOGO]

Thread, Interruption et variable de thread


La version d'Eclipse installée sur vos machines ne supportent pas la version 8 de Java, donc pour ce TP et les TPs suivant nous allons utiliser une version spécifique disponible ici
/home/ens/forax/java8/eclipse-lambda/eclipse
Eclipse fonctionnant sur le principe de workspace, il est plus simple d'utiliser un workspace pour java 7 et un workspace pour Java 8. Une fois Eclipse lancée, configurer le workspace utiliser par celui-ci pour qu'il corresponde à un répertoire workspace-eclipse8 dans votre répertoire personnel.
Enfin, en allant dans les préférences (Window / Preferencew), il faut indiquer qu'il faut utiliser le jdk8 disponible dans le répertoire
/home/ens/forax/java8/jdk1.8.0-lambda
dans Java / Installed JREs / Add... et demander au compilateur d'utiliser la syntaxe de Java 8 dans Java / Compiler / Compliance level.

Si vous voulez utiliser ces versions chez vous,

Exercice 1 - Hello Thread

On souhaite créer deux threads exécutant le même code. Pour différencier les deux threads, lors de la construction de celles-ci, un entier unique (id) sera fourni à chacune, 0 pour la première et 1 pour la seconde.
Chaque thread exécutera le même code qui consiste à afficher hello suivi du numéro de la thread ainsi que la valeur d'un compteur indiquant le nombre de fois que la thread a affiché ce message.
Par exemple, on pourra obtenir ce type d'affichage :
    ...
    hello 0 10714
    hello 0 10715
    hello 0 10716
    hello 0 10717
    hello 1 15096
    hello 1 15097
    hello 1 15098
    hello 1 15099
    ...
   

  1. Avant de commencer à coder, expliquer sur l'exemple ci-dessus pourquoi selon vous le compteur de hello 0 est beaucoup plus petit que le compteur de hello 1? Est-ce que cette observation sera la même à chaque exécution? Peut on le contrôler?
  2. Toujours avant de commencer à coder, rappeler à quoi sert l'interface Runnable ?
  3. Créer maintenant une classe HelloRunnable qui implante l'interface Runnable, qui prend à la construction un numéro d'identification et qui dans la méthode run fait une boucle en utilisant un index 1 à 10 000 qui affiche hello id index (comme dans l'exemple ci-dessus).
    Ecrire dans une autre classe HelloThread une méthode main qui crée deux HelloRunnable, un avec l'id 0 et un autre avec l'id 1, puis qui crée deux threads (un sur chaque runnable) et démarre ceux-ci en concurrence. Testez votre code dans la console d'eclipse et dans une fenêtre de terminal.
  4. Changer votre code pour que l'on puisse choisir lors de l'exécution du programme (1er argument, args[0]) le nombre de threads que l'on veut lancer en concurrence (si l'argument est 2, on aura le même comportement que précédemment).
    Rappel: Integer.parseInt prend une chaine de caractère en paramètre et renvoie un entier.
  5. Modifier le main de HelloThread pour utiliser la syntaxe des lambdas-expressions pour spécifier le Runnable plutôt que d'utiliser la classe HelloRunnable.
  6. Tester la méthode setPriority(int) de Thread.

Exercice 2 - Modification d'une variable en concurrence

On souhaite créer deux threads qui changent le même champ d'un même objet :

  1. Qu'affiche le code suivant avec la ligne de commande : java Test ?
    Expliquer.
  2. Pourquoi l'affichage n'évolue plus au bout d'un laps de temps ?
    Peut-on en déduire qu'il n'y a plus de problème de concurrence ?
  3. Comment doit on faire pour être sûr que chaque thread voit les modifications effectuées sur une variable par l'autre thread ?

Exercice 3 - Coitus interruptus

Sur la base de la classe HelloThread du premier exercice, on souhaite maintenant permettre à l'utilisateur, en tapant un nombre, d'interrompre la thread ayant cet identificateur.

  1. Avant tout, expliquer les différences entre les méthodes interrupted et isInterrupted de la classe Thread.
  2. Recopier le code de la classe HelloThread en une nouvelle classe Interruptus et utiliser un scanner (java.util.Scanner) pour lire sur l'entrée standard des commandes de contrôle (à partir de la thread principale).
    Pour éviter de surcharger la console par des affichages, on souhaite simplement que chaque thread incrémente un index et affiche lorsqu'elle est démarée et lorsqu'elle est interrompue. Le code que devra excuter chaque thread doit donc être le suivant (à compléter):
    int index;
    System.out.println("Thread " + Thread.currentThread().getName() + " started.");
    for(index = 0; /* something to complete here */; index++) {
      // do nothing
    }
    System.out.println("Thread " + Thread.currentThread().getName() + " has count till "
      + index + " before being interrupted.");
    

    Reste ensuite à implanter les commandes suivantes, lorsqu'elle sont lues sur le scanner :
    • kill id afin de tuer la thread d'identificateur id.
      On utilisera la méthode interrupt() pour interrompre la thread.
    • status id afin d'afficher l'état (state) de la thread d'identificateur id.
    Il y a deux façons d'arrêter le programme, soit toutes les threads ont été interrompues, soit l'utilisateur tape Control D dans le terminal (Scanner.hasNext() renvoi false)
    Il est possible de surveiller la machine virtuelle avec la commande jconsole (mémoire allouée sur le tas, classes chargées, threads en activité...).
    Lancer cette commande, sélectionner le processus à surveiller et vérifier que les threads sont bien interrompues.
  3. Plutôt que // do nothing, on souhaite maintenant que chaque thread affiche où elle en est de l'incrémentation de son compteur, mais pour éviter de remplir trop vite la console, on peut utiliser la méthode Thread.sleep() entre deux tours de boucle. Quel problème cela pose-t-il? Que doit on faire de l'exception levée par cette méthode?