Motivation
- Depuis Java 5, la plateforme propose des utilitaires de concurrence de haut niveau dans le package java.util.concurrent.
- Ils remplacent l'utilisation manuelle et complexe de wait and notify.
- Ils sont plus sûrs et plus faciles à utiliser, réduisant ainsi le risque d'erreurs dans le code concurrent.
Utilitaires de concurrence dans java.util.concurrent
Catégories d'utilitaires :
- Executor Framework : gestion des threads abordée dans l'article 80.
- Collections simultanées : implémentations thread-safe de collections standard telles que List, Queue et Map.
- Synchroniseurs : coordination entre les threads, notamment CountDownLatch, Semaphore, CyclicBarrier, Exchanger et Phaser.
Collections concurrentes
Caractéristiques :
- Synchronisé en interne pour des performances élevées.
- Ils ne permettent pas l'exclusion d'une activité concurrente.
- Les opérations atomiques comme putIfAbsent augmentent la sécurité et la convivialité.
Exemple : Implémentation d’une Map thread-safe :
Map<String, String> map = new ConcurrentHashMap<>();
String result = map.putIfAbsent("key", "value");
if (result == null) {
System.out.println("Valor inserido.");
} else {
System.out.println("Chave já existente com valor: " + result);
}
Copier après la connexion
Avantages :
- Remplacer les collections synchronisées (Collections.synchronizedMap).
- Amélioration significative des performances des applications concurrentes.
Synchroniseurs
Objectif : Coordination entre les threads.
Exemple de synchroniseurs courants :
- CountDownLatch : barrière à usage unique pour la coordination des threads.
- Sémaphore : Contrôler l'accès aux ressources partagées.
- CyclicBarrier : Synchronisation aux points de barrière réutilisables.
- Phaser : synchronisation avancée et dynamique des threads.
Exemple pratique : synchronisation simultanée avec CountDownLatch
Objectif : Mesurer le temps d'exécution de plusieurs threads simultanément.
Mise en œuvre :
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(() -> {
try {
ready.countDown(); // Indica que está pronto
start.await(); // Aguarda o sinal de início
action.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown(); // Indica que terminou
}
});
}
ready.await(); // Aguarda todas as threads ficarem prontas
long startTime = System.nanoTime();
start.countDown(); // Dispara o sinal de início
done.await(); // Aguarda todas as threads finalizarem
return System.nanoTime() - startTime;
}
Copier après la connexion
Remarques :
- Utilise trois loquets : prêt (indique l'état de préparation), démarrage (déclenchement initial) et terminé (finalisation).
- Utilise System.nanoTime pour mesurer avec précision les intervalles de temps.
Pratique actuelle avec attendre et notifier
Nécessaire uniquement pour la maintenance du code existant.
Règles principales :
- Utilisez toujours une boucle lorsque vous appelez wait :
synchronized (lock) {
while (!condition) {
lock.wait();
}
}
Copier après la connexion
- Testez la condition avant et après avoir attendu.
- Évitez de dépendre de notify, préférez notifyAll.
Conclusion
- Utilisez des utilitaires concurrents autant que possible.
- Ils rendent le code plus lisible, sécurisé et efficace.
- Des alternatives modernes (comme CyclicBarrier ou Phaser) peuvent remplacer les modèles basés sur l'attente et la notification
Exemples du livre
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!