[JAVA] Élément 81: Préférez les utilitaires de concurrence pour attendre et notifier
81 Sélectionnez l'utilitaire de concurrence à partir d'attendre et de notifier
- Depuis Java5, le traitement qui était auparavant effectué par attente et notification peut désormais être effectué avec un utilitaire de concurrence de haut niveau. Ceux-ci devraient être utilisés à la place.
- Les utilitaires d'accès concurrentiel de haut niveau peuvent être divisés en trois parties.
- Cadre d'exécution (Item80)
- collecte simultanée. C'est celui implémenté dans List, Map et Queue afin que le traitement parallèle puisse être correctement exécuté. Ceux-ci sont synchronisés en interne, donc leur verrouillage ralentit simplement le processus. Cette partie convient aux opérations de modification dépendant de l'état. Avec l'avènement des collectes simultanées, les collections synchronisées sont devenues une boîte de paiement.
- synchroniseurs. les synchroniseurs permettent des mouvements accentués par d'autres, comme l'attente de la fin du traitement des autres threads. Les plus célèbres sont CountDownLatch, Semaphore, CyclicBarrier, Exchanger et Phaser.
- Le code utilisant CountDownLatch ressemble à ceci: Pour expliquer la méthode du temps, lorsque ready est countDown trois fois, ready await est résolu, startNanos est mesuré, start countDown est fait une fois, l'action est exécutée et chaque thread est terminé countDown Ensuite, lorsque les trois comptes à rebours, l'attente terminée est résolue et le temps pris est renvoyé. Cette méthode ne se termine pas si le nombre de threads est inférieur au nombre spécifié dans le deuxième argument (thread famine deadlock).
package tryAny.effectiveJava;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
Executor e = Executors.newFixedThreadPool(3); //Si l'argument ici est inférieur à la valeur du deuxième argument de temps, le processus ne se terminera pas.
System.out.println(time(e, 3, () -> System.out.println("a")));
}
// Simple framework for timing concurrent execution
public static long time(Executor executor, int concurrency, Runnable action) throws InterruptedException {
CountDownLatch ready = new CountDownLatch(concurrency);
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(concurrency);
for (int i = 0; i < concurrency; i++) {
executor.execute(() -> {
ready.countDown(); // Tell timer we're ready
try {
start.await(); // Wait till peers are ready
action.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown(); // Tell timer we're done
}
});
}
ready.await(); // Wait for all workers to be ready
long startNanos = System.nanoTime();
start.countDown(); // And they're off!
done.await(); // Wait for all workers to finish
return System.nanoTime() - startNanos;
}
}
- Vous ne verrez plus attendre et notifier dans le code nouvellement écrit. Vous pouvez le voir dans la maintenance du code hérité.
- Si vous écrivez simplement un résumé sur l'attente et la notification,
- Utilisez wait in a while loop.
- Vous devez utiliser notifyAll plutôt que notifier. Lorsque vous utilisez notifier, vous devez faire attention à la survie du thread.