Article de reference

Java ConcurrentMap

Le framework Java Collections ( JEF) du langage de programmation Java , à partir de la version 1.5, définit et implémente les maps monothread classiques, ainsi que de nouvelles ...

framework Java Collections ( JEF) du langage de programmation Java , à partir de la version 1.5, définit et implémente les maps monothread classiques, ainsi que de nouvelles maps thread-safe implémentant l' interface `maps` parmi d'autres interfaces concurrentes. En Java 1.6, l' interface `maps` a été ajoutée, étendant `maps` , et l' interface `maps` a été ajoutée en tant que combinaison de sous-interfaces.table de hachage contenant des listes d'entrées. Chaque entrée comprend une clé, une valeur, le hachage et une référence à l'entrée suivante. Avant Java 8, plusieurs verrous étaient utilisés, chacun sérialisant l'accès à un « segment » de la table. Dans Java 8, la synchronisation native est appliquée aux têtes des listes elles-mêmes, et ces listes peuvent se transformer en petits arbres lorsqu'elles risquent de devenir trop volumineuses en raison de collisions de hachage. De plus, Java 8 utilise la primitive de comparaison et d'affectation de manière optimiste pour placer les têtes initiales dans la table, ce qui est très rapide. Les performances sont excellentes , mais des délais peuvent survenir occasionnellement lors du rehachage. Une fois la table de hachage agrandie, elle ne se réduit jamais, ce qui peut entraîner une fuite de mémoire après la suppression d'entrées.

ConcurrentSkipListMap

Pour un accès ordonné tel que défini par l' java.util.NavigableMap<K, V>interface, java.util.concurrent.ConcurrentSkipListMap<K, V>une classe a été ajoutée en Java 1.6 et java.util.concurrent.ConcurrentMap<K, V>implémente les interfaces `getOrder` et ` getStop` java.util.concurrent.ConcurrentNavigableMap<K, V>. Il s'agit d'une liste à sauts utilisant des techniques sans verrou pour construire un arbre. Ses performances sont de 10⁻¹⁰ .

Problème de modification simultanée

L'un des problèmes résolus par le java.util.concurrent<K, V>package Java 1.5 est celui de la modification concurrente. Les classes de collections qu'il fournit peuvent être utilisées de manière fiable par plusieurs java.lang.Threadinstances.

Toutes les collections et maps non concurrentes partagées entre threads doivent utiliser une forme de verrouillage explicite, comme la synchronisation native, afin d'empêcher toute modification concurrente. À défaut, il doit exister un moyen de prouver, par la logique du programme, que toute modification concurrente est impossible. La modification concurrente d'une collection java.lang.Map<K, V>par plusieurs threads peut parfois détruire la cohérence interne des structures de données qu'elle contient java.lang.Map<K, V>, entraînant des bogues rares ou imprévisibles, difficiles à détecter et à corriger. De même, une modification concurrente par un thread, avec un accès en lecture par un ou plusieurs autres threads, peut parfois produire des résultats imprévisibles pour le lecteur, même si la cohérence interne de la map n'est pas compromise. L'utilisation d'une logique externe pour empêcher les modifications concurrentes accroît la complexité du code et crée un risque d'erreurs imprévisible dans le code existant et futur, bien qu'elle permette l'utilisation de collections non concurrentes. Cependant, ni les verrous ni la logique externe ne peuvent coordonner les threads externes susceptibles d'interagir avec la collection java.util.Collection<E>.

Compteurs de modification

Pour pallier le problème des modifications concurrentes, les java.lang.Map<K, V>implémentations non concurrentes et d'autres java.util.Collection<E>mécanismes utilisent des compteurs de modifications internes. Ces compteurs sont consultés avant et après chaque lecture afin de détecter les changements : les processus d'écriture incrémentent ces compteurs. Ce mécanisme est censé détecter une modification concurrente en levant une exception , "valeur" ); }// Thread B synchronisé ( map ) { String résultat = map . get ( "clé" ); // ... }// Thread C synchronized ( map ) { for ( Map . Entry < String , String > s : map . entrySet ()) { /* * Opération potentiellement lente, retardant toutes les autres opérations supposément rapides. * La synchronisation sur les itérations individuelles n'est pas possible. */ // ... } }

Verrou de lecture/écriture réentrant

Le code utilisant un verrou key" , " value" ); } finally { writeLock.unlock ( ) ; }// Thread B try { readLock . lock (); String s = map . get ( "key" ); } finally { readLock . unlock (); }// Thread C try { readLock.lock ( ); for ( Map.Entry < String , String > s : map.entrySet ( ) ) { /* * Opération potentiellement lente, retardant toutes les autres opérations supposément rapides. * La synchronisation sur les itérations individuelles n'est pas possible. */ // ... } } finally { readLock.unlock ( ) ; }

Convois

L'exclusion mutuelle présente un problème de congestion des verrous , où les threads peuvent s'accumuler sur un verrou, obligeant la JVM à maintenir des files d'attente coûteuses et à « mettre en attente » les java.lang.Threadprocessus en attente. Ces opérations sont onéreuses et peuvent entraîner un changement de contextejava.lang.Thread lent . Les changements de contexte prennent de la microseconde à la milliseconde, tandis que les opérations de base de la carte s'effectuent généralement en nanosecondes. Les performances peuvent chuter à une fraction du débit d'une seule tâche lorsque la contention augmente. En l'absence de contention ou en cas de faible contention du verrou, l'impact sur les performances est minime, sauf pour le test de contention du verrou. Les JVM modernes intègrent la majeure partie du code de verrouillage, le réduisant à quelques instructions seulement, ce qui garantit une grande rapidité en l'absence de contention. Les techniques réentrantes, telles que la synchronisation native, ont un coût supplémentaire en termes de performances lié à la gestion de la profondeur de réentrance, affectant également le cas sans contention. Le problème de l'enchaînement de requêtes semble s'atténuer avec les JVM modernes, mais il peut être masqué par des changements de contexte lents : dans ce cas, la latence augmente, mais le débit reste acceptable. Avec des centaines de requêtes, un temps de changement de contexte de 10 ms induit une latence de plusieurs secondes.java.lang.Threadjava.util.concurrent.locks.ReentrantReadWriteLockjava.lang.Thread

Plusieurs cœurs

Les solutions d'exclusion mutuelle ne tirent pas pleinement parti de la puissance de calcul d'un système multicœur, car un seul processus java.lang.Threadest autorisé java.util.Map<K, V>à la fois dans le code. Les implémentations des maps concurrentes fournies par le Java Collections Framework (JCF) et d'autres frameworks exploitent parfois les multicœurs grâce à des techniques de programmation sans verrouillage . Ces techniques utilisent des opérations telles que la compareAndSet()méthode intrinsèque disponible sur de nombreuses classes Java, permettant par exemple

Les tables de hachage sont toutes deux rapides

Seules les cartes ordonnées sont mises à l'échelle, et la carte synchronisée revient à sa taille initiale.La carte synchronisée est revenue à un état similaire aux cartes ordonnées à l'échelle.

latence prévisible

Un autre problème des approches d'exclusion mutuelle réside dans le fait que l'hypothèse d'atomicité complète, sous-jacente à certains codes monothread, engendre des délais inter-threads sporadiques et inacceptables dans un environnement concurrent. En particulier, les itérateurs et les opérations groupées, comme ` putAll()map` et `filter`, peuvent prendre un temps proportionnel à leur java.util.Map<K, V>taille, retardant ainsi d'autres opérations qui s'attendent à une latence faible et prévisible pour les opérations non groupées. Par exemple, un serveur webjava.lang.Thread multithread ne peut tolérer que certaines réponses soient retardées par de longues itérations d'autres threads exécutant des requêtes à la recherche d'une valeur particulière. De plus, les opérations qui verrouillent un objet n'ont aucune obligation de libérer ce verrou, et une boucle infinie dans l'opération propriétaire peut propager un blocage permanent à d'autres opérations. Les opérations propriétaires lentes peuvent parfois être interrompues. Les tables de hachage sont également sujettes à des délais spontanés lors du rehachage.java.lang.Threadjava.util.Map<K, V>Threadjava.lang.Threadjava.lang.Thread

Faible cohérence

La java.util.concurrentsolution apportée par les paquets aux problèmes de modification concurrente, de convoi, de latence prévisible et de multicœur repose sur un choix architectural appelé cohérence faible. Ce choix implique que les lectures sans verrou pour y parvenir, et les implémentations existantes seront automatiquement atomiques. Ces techniques sans verrou peuvent être plus lentes que les surcharges dans les classes concrètes ; ces dernières peuvent donc choisir de les implémenter de manière atomique ou non et documenter leurs propriétés de concurrence.computeIfPresent(K, BiFunction)compute(K,BiFunction)merge(K, V, BiFunction)java.util.Map<K, V>java.util.concurrent.ConcurrentMap<K, V>java.util.concurrent.ConcurrentMap<K, V>

atomicité sans verrouillage

Il est possible d'utiliser des techniques sans verrouillage avec java.util.concurrent.ConcurrentMap<K, V>les Maps car elles incluent des méthodes avec un nombre de consensus suffisamment élevé, à savoir l'infini , ce qui signifie que n'importe quel nombre de java.lang.ThreadMaps peut être coordonné. Cet exemple pourrait être implémenté avec Java 8 merge(), mais il illustre le modèle global sans verrouillage, qui est plus général. Cet exemple ne concerne pas le fonctionnement interne des Maps, java.util.concurrent.ConcurrentMap<K, V>mais leur utilisation par le code client java.util.concurrent.ConcurrentMap<K, V>. Par exemple, si nous voulons multiplier atomiquement une valeur de la Map par une constanteN :

Joshua Bloch et a été introduit dans JDK 1.2 . Les classes de concurrence originales proviennent du package de collections de Doug Lea .

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index