Contenu du cours
Multithreading en Java
Multithreading en Java
ConcurrentMap et Ses Implémentations
Exemple de la vie réelle
Une application web utilise ConcurrentMap
pour mettre en cache les données fréquemment demandées telles que les sessions utilisateur. Différents threads peuvent simultanément mettre à jour et lire les données de la carte, assurant un accès rapide et des opérations sécurisées.
Différences par rapport à d'autres types
- Sécurité dans un environnement multi-thread :
ConcurrentMap
gère automatiquement la synchronisation des accès aux données, tandis que dans unMap
conventionnel, cette tâche doit être effectuée manuellement ; - Efficacité : Permet de lire et écrire des données en parallèle sans verrouiller l'ensemble de la structure de données.
Implémentations de ConcurrentMap
ConcurrentHashMap
: Prend en charge efficacement plusieurs threads en divisant la carte en segments (buckets), permettant l'exécution parallèle des opérations sans verrouiller l'ensemble de la carte.
Syntaxe
Main
ConcurrentMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap
en Java divise les données en plusieurs buckets, chacun géré par un moniteur distinct. Cette configuration permet à différents threads de modifier ou de lire des données de différents buckets simultanément, ce qui améliore les performances.
Les threads peuvent accéder aux buckets en parallèle, réduisant les verrous et évitant les conflits de données.
Chaque bucket contient des enregistrements sous forme de paires clé-valeur, qui peuvent être organisés en listes chaînées.
ConcurrentSkipListMap
: Une implémentation basée sur une skip-list qui prend en charge l'ordre des clés triées. Fournit une insertion, une suppression et un accès aux données rapides dans un environnement multithread.
Syntaxe
Main
ConcurrentMap<Integer, Integer> concurrentSkipListMap = new ConcurrentSkipListMap<>();
📝Insertion : Lorsqu'un nouvel élément est ajouté à ConcurrentSkipListMap
, il commence au niveau le plus bas. Il monte ensuite à travers les niveaux jusqu'à ce qu'il soit placé là où ses clés et valeurs sont dans le bon ordre.
🔍Recherche : Pour trouver un élément par clé, ConcurrentSkipListMap
commence au nœud de tête du niveau le plus haut. Il suit les pointeurs jusqu'à ce qu'il localise un nœud avec une clé égale ou supérieure à la clé de recherche.
❌Suppression : Pour supprimer un élément de ConcurrentSkipListMap
, il est d'abord retiré du niveau le plus bas. Il est ensuite déclassé à travers les niveaux jusqu'à ce qu'il soit retiré de l'endroit où ses clés et valeurs sont correctement ordonnées.
Exemple d'utilisation de ConcurrentMap dans le code
Méthodes Principales
putIfAbsent(K key, V value)
: Ajoute une paire clé-valeur à la map uniquement si la clé n'est pas déjà présente.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.putIfAbsent("a", 1); // Adds the pair ("a", 1) to the map, as "a" is not already present map.putIfAbsent("a", 2); // Does not change the value, as "a" is already present in the map
remove(Object key, Object value)
: Supprime la paire clé-valeur si la clé est associée à la valeur spécifiée.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.remove("a", 1); // Removes the pair ("a", 1), as "a" is mapped to value 1 map.remove("a", 2); // Does nothing, as "a" is not mapped to value 2
replace(K key, V value)
: Remplace l'entrée pour une clé uniquement si elle est actuellement associée à une valeur.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.replace("a", 2); // Replaces the value 1 with 2 for key "a" map.replace("b", 3); // Does nothing, as "b" is not present
compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
: Calcule une nouvelle valeur pour la clé spécifiée en utilisant la fonction de remappage donnée, ce qui peut impliquer de créer une nouvelle valeur, de modifier ou de supprimer la valeur existante.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.compute("a", (k, v) -> v == null ? 1 : v + 1); // Increases the value for key "a" by 1 map.compute("b", (k, v) -> v == null ? 1 : v + 1); // Sets the value to 1 for new key "b"
merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
: Fusionne la valeur donnée avec la valeur existante associée à la clé en utilisant la fonction de remappage fournie, ce qui aide à agréger les données.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); map.merge("a", 2, Integer::sum); // Sums the current value (1) with the new value (2), resulting in 3 map.merge("b", 2, Integer::sum); // Sets the value to 2 for new key "b"
getOrDefault(Object key, V defaultValue)
- retourne la valeur associée à la clé spécifiée, ou la valeur par défaut si la clé est absente.
Main
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("a", 1); int value1 = map.getOrDefault("a", 0); // Returns 1, as "a" is present int value2 = map.getOrDefault("b", 0); // Returns 0, as "b" is not present
😔 Limitations
L'un des inconvénients potentiels est l'instabilité de l'ordre, car certaines implémentations pourraient ne pas garantir l'ordre des éléments lors de l'itération. De plus, il peut y avoir un support limité pour certaines opérations ; par exemple, les mises à jour conditionnelles atomiques peuvent ne pas être entièrement prises en charge dans certaines implémentations.
💪 Avantages
Du côté positif, la haute performance est un avantage clé, ce qui le rend bien adapté aux scénarios impliquant des opérations intensives de lecture et d'écriture. Il offre également une facilité d'utilisation, réduisant considérablement le besoin de gestion manuelle de la synchronisation dans un environnement multi-thread.
1. Quel est le but de ConcurrentMap ?
2. Laquelle des implémentations suivantes est une implémentation thread-safe de ConcurrentMap ?
3. Comment ConcurrentHashMap assure-t-il la sécurité des threads ?
Merci pour vos commentaires !