advanced.java
← Tous les concepts
Concurrence · ch. 9, p. 74

Collections concurrentes et wait/notify

ConcurrentHashMap et les files bloquantes offrent un accès thread-safe sans verrou global. wait/notify est la primitive de bas niveau sous-jacente, et c'est presque toujours le mauvais outil aujourd'hui.

Collections.synchronizedMap sérialise chaque opération sur un seul verrou. ConcurrentHashMap verrouille par compartiment, donc les lecteurs ne bloquent jamais et les écrivains n’entrent en contention qu’en cas de collision.

ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
counts.merge(key, 1, Integer::sum);            // atomique
counts.computeIfAbsent(key, k -> load(k));     // calculé au plus une fois

Les opérations composées construites à partir de get puis put ne sont pas atomiques, même sur une map concurrente. Utilisez merge, compute, computeIfAbsent ou putIfAbsent.

Sémantique d’itération

Les collections concurrentes offrent des itérateurs faiblement cohérents : elles ne lancent jamais ConcurrentModificationException et peuvent refléter ou non les mises à jour concurrentes. CopyOnWriteArrayList prend un instantané à l’itération — idéal pour les listes d’écouteurs, désastreux pour des données à forte écriture.

Producteur/consommateur

BlockingQueue<Task> queue = new ArrayBlockingQueue<>(1000);
queue.put(task);          // bloque quand pleine — contre-pression gratuite
Task t = queue.take();    // bloque quand vide

wait/notify, si vraiment nécessaire

synchronized (lock) {
    while (!condition) {      // toujours une boucle, jamais un if
        lock.wait();          // les réveils intempestifs sont permis
    }
}

N’appelez cela qu’en détenant le moniteur, revérifiez toujours le prédicat dans une boucle, et préférez notifyAllnotify réveille un seul attendeur arbitraire et peut en laisser d’autres bloqués. Dans du code neuf, préférez une BlockingQueue, un CountDownLatch ou un Semaphore.

2 questions sur ce concept S'entraîner