:: Enseignements :: Master :: M1 :: 2019-2020 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Synchronized, lock, trylock, interrupt |
Exercice 1 - Tableau d'honneur
Une école possède un tableau d'honneur (où l'on met le nom de l'élève le plus méritant) qui
peut être mis à jour de façon informatique. Il n'en faut pas plus pour que John Doe et Jane Odd,
nos deux apprentis hackers, écrivent un petit programme qui met à jour automatiquement
le tableau d'honneur avec leurs noms.
Malheureusement, la classe
HonorBoard n'est pas
thread safe, donc le code
fait n'importe quoi.
-
Rappeler ce que cela veut dire qu'une classe n'est pas thread safe.
-
Expliquer pourquoi la classe HonorBoard n'est pas thread safe.
Si vous ne voyez pas, faîtes un grep "John Odd" sur la sortie du programme.
Rappel général: un test qui plante indique un problème, un test qui ne plante pas
n'indique rien du tout.
-
Modifiez le code de la classe HonorBoard pour la rendre thread safe
en utilisant des blocs synchronized.
Pensez à vérifier avec grep sur la sortie comme précédemment
(pendant plusieurs minutes).
-
Maintenant que votre classe est thread safe, peut-on remplacer la ligne:
System.out.println(board);
par la ligne:
System.out.println(board.getFirstName() + ' ' + board.getLastName());
avec les deux getters définis comme d'habitude ?
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
Exercice 2 - Tableau d'honneur (2)
Reprendre le code de la classe
HonorBoard de l'exercice précédent.
On cherche maintenant à remplacer les usages du mot-clé
synchronized par un verrou ré-entrant
du package
java.util.concurrent.lock.
-
Rappeler ce que ré-entrant veut dire.
-
Rendre la classe HonorBoard thread safe en utilisant un verrou ré-entrant.
Attention à bien libérer le verrou si une exception est levée.
Exercice 3 - Coitus interruptus
Dans cet exercice, on cherche à comprendre le fonctionnement des interruptions.
-
Pourquoi n'est il pas possible d’arrêter une thread de façon non coopérative ?
-
Qu'est-ce qu'une opération bloquante?
À quoi sert la méthode d'instance interrupt() de la classe Thread?
Expliquer comment interrompre une thread en train d'effectuer une opération bloquante et le faire sur l'exemple suivant (en faisant afficher "end" quand la thread est interrompue).
public static void main(String[] args) throws InterruptedException {
var thread = new Thread(() -> {
while(true){
...
Thread.sleep(1_000);
...
}
});
thread.start();
Thread.sleep(1_000);
thread.interrupt();
}
-
Expliquer, sur l'exemple suivant, comment utiliser la méthode
Thread.interrupted (noter qu'elle est statique)
pour arrêter une thread dans le cas où il n'y a pas d'opération bloquante (dans un premier temps, on suppose que vous ne pouvez pas modifier le code de slow). Faire afficher "end" et la valeur de forNothing quand la thread est interrompue.
private static int slow() {
var result = 1;
for (var i = 0; i < 1_000_000; i++) {
result += (result * 7) % 513;
}
return result;
}
public static void main(String[] args) throws InterruptedException {
var thread = new Thread(() -> {
var forNothing = 0;
while (true) {
...
forNothing += slow();
...
}
});
thread.start();
Thread.sleep(1_000);
thread.interrupt();
}
-
Et si vous pouvez modifier le code de slow (mais pas sa signature), que pouvez-vous faire?
-
Comment faire pour interrompre la thread à coup sûr, si elle effectue à la fois des opérations bloquantes et non bloquantes? Par exemple:
public static void main(String[] args) throws InterruptedException {
var thread = new Thread(() -> {
var forNothing = 0;
while(...) {
forNothing += slow();
...
Thread.sleep(1_000);
...
forNothing += slow();
}
});
thread.start();
Thread.sleep(1_000);
thread.interrupt();
}
-
Et si vous pouvez modifier le code de slow ET sa signature, que faites-vous?
-
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 ?
-
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(var scanner = new Scanner(System.in)) {
while(scanner.hasNextInt()) {
var 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.
-
Comment faire pour que le programme se termine si l'on fait un Ctrl-D dans le terminal ?
Exercice 4 - Le déjeuner des philosophes [Optionnel]
La situation est la suivante :
Cinq philosophes (initialement, mais il peut y en avoir beaucoup plus) se trouvent autour d'une table ronde;
chaque philosophe a, devant lui, un plat de spaghetti et entre chaque assiette se trouve une fourchette.
Pour pouvoir manger, un philosophe doit prendre deux fourchettes, celle à sa droite et celle à sa gauche.
Un de vos collègues propose le code suivant:
-
Quel est le problème du code ci-dessus ?
Dans quelle(s) condition(s) se produit-il ?
Note: vous avez le droit de l'exécuter si vous ne voyez pas.
-
Est-il possible d'avoir deux philosophes qui mangent en même temps ?
Est-ce quelque chose qui est normal ou pas ?
-
Modifier le code pour corriger le problème.
Note: il existe deux façons, une plus belle que l'autre :)
© Université de Marne-la-Vallée