Pour l'instant, nous ne gérons pas les InteruptedException
et rien dans votre code ne peut provoquer la levée de cette exception.
throws
),Runnable run = () -> { try { Thread.sleep(20_000); } catch (InterruptedException e) { // this should not happen ! throw new AssertionError(e); } };
On ne doit jamais voir e.printStackTrace();
BoundedSafeQueue
public class BoundedSafeQueue<V> { private final ArrayDeque<V> queue = new ArrayDeque<>(); private final int capacity; ... public void add(V value) throws InterruptedException { Objects.requireNonNull(value); synchronized (queue) { while (queue.size() >= capacity) { queue.wait(); } queue.add(value); queue.notify(); } } public V take() throws InterruptedException { synchronized (queue) { while (queue.isEmpty()) { queue.wait(); } queue.notify(); return queue.remove(); } }
Quel est le problème?
Supposons que 2 threads nommés t1 et t2 font des add
et qu'un 3e thread, le main, fait des take
, en continu, sur une file de capacité 1.
public class BoundedSafeQueue<V> { private final ArrayDeque<V> queue = new ArrayDeque<>(); private final int capacity; ... public void add(V value) throws InterruptedException { Objects.requireNonNull(value); synchronized (queue) { while (queue.size() >= capacity) { queue.wait(); } queue.add(value); queue.notifyAll(); } } public V take() throws InterruptedException { synchronized (queue) { while (queue.isEmpty()) { queue.wait(); } queue.notifyAll(); return queue.remove(); } }
Correction rapide
Dans cet exercice, on veut réaliser une classe Vote
. Cette classe thread-safe permet d'enregistrer
n
votes pour des String
. Quand les n
votes ont été enregistrés, on renvoie à chaque votant la String
qui a reçu le plus de votes (en cas d'égalité, on renvoie la plus petite dans l'ordre alphabétique ayant reçu le plus de votes). Le nombre n
de votes attendus est pris en paramètre par le constructeur.
La classe Vote
offre une seule méthode vote
qui sert à proposer son vote et qui bloque jusqu'à ce que les n
votes soient arrivés. Ensuite elle
renvoie le gagnant. Si vote
est appelée après que les n
votes aient été reçus, la méthode renvoie simplement le gagnant.
On calcule le gagnant après que le vote soit fini (et que d'autres votants peuvent arriver) : si un votant arrive après la fin du vote, il peut modifier le résultat !
On recalcule le gagnant à chaque fois qu'une valeur est renvoyée.
On fait trop de notifications : si chaque thread qui sort d'un wait
notifie tous les autres.
HashMap
Pour mettre à jour une HashMap<K,V>
efficacement (V non mutable) :
map.merge(key, 1, Interger::sum); // ou map.merge(key, 1, (x,y) -> x + y);
équivaut à :
map.compute(key,(k, v) -> { if (v == null) { return 1; } else { return v + 1; } });
HashMap
Au pire, si vous n'y arrivez pas avec une lambda (V toujours non mutable):
var oldValue = map.get(key); // fait office d'appel à map.contains(key) if (oldValue == null) { oldValue = initialValue; } V updatedValue = f(oldValue); map.put(key, updatedValue);
Ou mieux :
var oldValue = map.getOrDefault(key,0); V updatedValue = f(oldValue); map.put(key, updatedValue);
HashMap
Pour mettre à jour une HashMap<K,V>
efficacement (V mutable):
HashMap<Long,Data> map = ... var data = map.computeIfAbsent<(key, k -> new Data()); data.update(newValue);
Par exemple :
HashMap<String,ArrayList<Integer>> map = ... var data = map.computeIfAbsent(key, k -> new ArrayList<>()); data.add(1);