Kursinhalt
Multithreading in Java
Multithreading in Java
ConcurrentMap und Seine Implementierungen
Beispiel aus dem echten Leben
Eine Webanwendung verwendet ConcurrentMap
, um häufig angeforderte Daten wie Benutzersitzungen im Cache zu speichern. Verschiedene Threads können gleichzeitig Daten aktualisieren und lesen, was schnellen Zugriff und sichere Operationen gewährleistet.
Unterschiede zu anderen Typen
- Sicherheit in einer Multi-Thread-Umgebung:
ConcurrentMap
übernimmt automatisch die Synchronisation von Datenzugriffen, während diese Aufgabe bei herkömmlichenMap
manuell erledigt werden muss; - Effizienz: Ermöglicht das Lesen und Schreiben von Daten parallel, ohne die gesamte Datenstruktur zu sperren.
Implementierungen von ConcurrentMap
ConcurrentHashMap
: Unterstützt effizient mehrere Threads, indem die Karte in Segmente (Buckets) unterteilt wird, was parallele Ausführung von Operationen ohne Sperrung der gesamten Karte ermöglicht.
Syntax
Main
ConcurrentMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap
in Java teilt Daten in mehrere Buckets auf, die jeweils von einem separaten Monitor verwaltet werden. Diese Konfiguration ermöglicht es verschiedenen Threads, Daten aus verschiedenen Buckets gleichzeitig zu ändern oder zu lesen, was die Leistung verbessert.
Threads können parallel auf Buckets zugreifen, wodurch Sperren reduziert und Datenrennen vermieden werden.
Jeder Bucket enthält Datensätze in Form von Schlüssel-Wert-Paaren, die als verkettete Listen organisiert werden können.
ConcurrentSkipListMap
: Eine Skip-List-basierte Implementierung, die sortierte Schlüsselreihenfolge unterstützt. Bietet schnelle Einfügung, Löschung und Zugriff auf Daten in einer Multithread-Umgebung.
Syntax
Main
ConcurrentMap<Integer, Integer> concurrentSkipListMap = new ConcurrentSkipListMap<>();
📝Einfügen: Wenn ein neues Element zu ConcurrentSkipListMap
hinzugefügt wird, beginnt es auf der untersten Ebene. Es bewegt sich dann nach oben durch die Ebenen, bis es dort platziert wird, wo seine Schlüssel und Werte in der richtigen Reihenfolge sind.
🔍Suche: Um ein Element nach Schlüssel zu finden, beginnt ConcurrentSkipListMap
am Kopfknoten der obersten Ebene. Es folgt den Zeigern, bis es einen Knoten mit einem Schlüssel findet, der gleich oder größer als der Suchschlüssel ist.
❌Löschen: Um ein Element aus ConcurrentSkipListMap
zu löschen, wird es zuerst aus der untersten Ebene entfernt. Es wird dann herabgestuft durch die Ebenen, bis es entfernt wird, wo seine Schlüssel und Werte korrekt geordnet sind.
Beispiel für die Verwendung von ConcurrentMap im Code
Hauptmethoden
putIfAbsent(K key, V value)
: Fügt ein Schlüssel-Wert-Paar zur Map hinzu, nur wenn der Schlüssel noch nicht vorhanden ist.
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)
: Entfernt das Schlüssel-Wert-Paar, wenn der Schlüssel auf den angegebenen Wert abgebildet ist.
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)
: Ersetzt den Eintrag für einen Schlüssel nur, wenn er derzeit auf einen Wert abgebildet ist.
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)
: Berechnet einen neuen Wert für den angegebenen Schlüssel unter Verwendung der angegebenen Remapping-Funktion, die möglicherweise das Erstellen eines neuen Wertes, Ändern oder Entfernen des vorhandenen Wertes beinhaltet.
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)
: Vereint den gegebenen Wert mit dem vorhandenen Wert, der dem Schlüssel zugeordnet ist, unter Verwendung der bereitgestellten Remapping-Funktion, die bei der Datenaggregation hilft.
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)
- gibt den Wert zurück, der dem angegebenen Schlüssel zugeordnet ist, oder den Standardwert, wenn der Schlüssel nicht vorhanden ist.
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
😔 Einschränkungen
Ein potenzieller Nachteil ist die Ordnungsinstabilität, da einige Implementierungen möglicherweise nicht die Reihenfolge der Elemente während der Iteration garantieren. Zusätzlich kann es eine eingeschränkte Unterstützung für bestimmte Operationen geben; zum Beispiel werden atomare bedingte Aktualisierungen möglicherweise nicht in allen Implementierungen vollständig unterstützt.
💪 Vorteile
Auf der positiven Seite ist die hohe Leistung ein wesentlicher Vorteil, was es gut geeignet macht für Szenarien mit intensiven Lese- und Schreiboperationen. Es bietet auch Benutzerfreundlichkeit, was den Bedarf an manueller Synchronisationsverwaltung in einer Multi-Thread-Umgebung erheblich reduziert.
1. Was ist der Zweck von ConcurrentMap?
2. Welche der folgenden ist eine threadsichere Implementierung von ConcurrentMap?
3. Wie stellt ConcurrentHashMap die Thread-Sicherheit sicher?
Danke für Ihr Feedback!