Was ist der Unterschied zwischen ConcurrentHashMap und Collections.synchronizedMap (Map)?

607

Ich habe eine Map, die von mehreren Threads gleichzeitig geändert werden soll.

Es scheint drei verschiedene synchronisierte Map-Implementierungen in der Java-API zu geben:

  • Hashtable
  • Collections.synchronizedMap(Map)
  • ConcurrentHashMap

Soweit ich weiß, Hashtablehandelt es sich um eine alte Implementierung (Erweiterung der veralteten DictionaryKlasse), die später an die MapSchnittstelle angepasst wurde . Während es ist synchronisiert, so scheint es ernst zu haben Skalierbarkeitsprobleme und wird für neue Projekte abgeraten.

Aber was ist mit den anderen beiden? Was sind die Unterschiede zwischen von Collections.synchronizedMap(Map)und ConcurrentHashMaps zurückgegebenen Karten ? Welches passt zu welcher Situation?

Henning
quelle
7
@ SmilesinaJar Link ist derzeit defekt, hier ist eine archivierte Kopie dieses Artikels: Warum ConcurrentHashMap besser als Hashtable und genauso gut wie eine HashMap ist
informatik01
2
IBM: Wie ConcurrentHashMap eine höhere Parallelität bietet, ohne die Thread-Sicherheit zu beeinträchtigen @ ibm.com/developerworks/java/library/j-jtp08223/…
pramodc84
Zu Ihrer Information, Java 6 wurde ConcurrentSkipListMapals weitere thread-sichere MapImplementierung eingeführt. Entwickelt, um unter Last mit dem Skip List- Algorithmus sehr gleichzeitig zu sein .
Basil Bourque

Antworten:

423

Verwenden Sie für Ihre Bedürfnisse ConcurrentHashMap. Es ermöglicht die gleichzeitige Änderung der Map von mehreren Threads aus, ohne dass diese blockiert werden müssen. Collections.synchronizedMap(map)Erstellt eine blockierende Map, die die Leistung beeinträchtigt, jedoch die Konsistenz gewährleistet (wenn sie ordnungsgemäß verwendet wird).

Verwenden Sie die zweite Option, wenn Sie die Datenkonsistenz sicherstellen möchten und jeder Thread eine aktuelle Ansicht der Karte haben muss. Verwenden Sie die erste Option, wenn die Leistung kritisch ist und jeder Thread nur Daten in die Karte einfügt, wobei Lesevorgänge seltener auftreten.

Yuval Adam
quelle
8
Mit Blick auf den Quellcode ist die synchronisierte Map nur eine Implementierung mit einem Mutex (Blockieren), während die ConcurrentHashMap für den gleichzeitigen Zugriff komplexer ist
Vinze
123
Bitte beachten Sie auch, dass die ConcurrentHashMap keine Nullschlüssel oder -werte zulässt. Sie sind also KEINE gleichwertigen Alternativen einer synchronisierten Karte.
Onejigtwojig
5
@AbdullahShaikh Das in diesem Artikel angesprochene Problem wurde in Java 7 behoben und weitere Verbesserungen wurden in Java 8 vorgenommen.
puls0ne
5
@hengxin: Sobald Sie einen Vorgang ausführen, der aus mehreren Abfragen oder Aktualisierungen der Karte besteht, oder wenn Sie über die Karte iterieren, müssen Sie die Karte manuell synchronisieren, um die Konsistenz sicherzustellen. Die synchronisierten Karten garantieren Konsistenz nur für einzelne Operationen (Methodenaufrufe) auf der Karte, was sie mehr als oft wertlos macht, da die meisten Operationen im wirklichen Leben nicht trivial sind und Sie ohnehin manuell synchronisieren müssen.
Holger
241
╔═══════════════╦═══════════════════╦═══════════════════╦═════════════════════╗
║   Property    ║     HashMap       ║    Hashtable      ║  ConcurrentHashMap  ║
╠═══════════════╬═══════════════════╬═══════════════════╩═════════════════════╣ 
║      Null     ║     allowed       ║              not allowed                ║
║  values/keys  ║                   ║                                         ║
╠═══════════════╬═══════════════════╬═════════════════════════════════════════╣
║ Thread-safety ║                   ║                                         ║
║   features    ║       no          ║                  yes                    ║
╠═══════════════╬═══════════════════╬═══════════════════╦═════════════════════╣
║     Lock      ║       not         ║ locks the whole   ║ locks the portion   ║        
║  mechanism    ║    applicable     ║       map         ║                     ║ 
╠═══════════════╬═══════════════════╩═══════════════════╬═════════════════════╣
║   Iterator    ║               fail-fast               ║ weakly consistent   ║ 
╚═══════════════╩═══════════════════════════════════════╩═════════════════════╝

In Bezug auf den Verriegelungsmechanismus: Hashtable Verriegelt das Objekt , während nur der EimerConcurrentHashMap verriegelt wird .

Sergii Shevchyk
quelle
13
Hashtablesperrt keinen Teil der Karte. Schauen Sie sich die Implementierung an. Es wird ein synchronizedSchlüssel ohne Schloss verwendet, was im Grunde bedeutet, dass er hashtablebei jedem Vorgang vollständig verriegelt wird .
RMachnik
6
Was ist mit der synchronizedMap?
Samuel Edwin Ward
3
Collections.syncronizedMap Verhalten ist wie die Backing Map, außer dass alle Methoden threadsicher sind
Sergii Shevchyk
5
Ich würde die Tabelle drucken und sie für jeweils 5 Dollar verkaufen;). Gute @shevchyk
realPK
Bearbeitet: Weder sind vollständig threadsicher. Das ist für neuere Entwickler etwas irreführend. Unter: ibm.com/developerworks/java/library/j-jtp07233/index.html erfahren Sie , dass selbst ConcurrentHashMap vor externen Datenrennen nicht vollständig threadsicher ist. (zB: 1 ​​Thread entfernt einen Wert und ein anderer versucht später zu überprüfen, ob er vorhanden ist, und ihn zu setzen, falls nicht. Dies ist eine Datenrennbedingung und bedeutet immer noch, dass Sie trotz Verwendung einer "ConcurrentHashMap" nicht alle Thread-Sicherheitsprobleme gelöst haben.
Zombies
142

Die "Skalierbarkeitsprobleme" für Hashtablesind in genau der gleichen Weise vorhanden Collections.synchronizedMap(Map)- sie verwenden eine sehr einfache Synchronisation, was bedeutet, dass nur ein Thread gleichzeitig auf die Karte zugreifen kann.

Dies ist kein großes Problem, wenn Sie einfache Einfügungen und Suchvorgänge haben (es sei denn, Sie tun dies äußerst intensiv), wird jedoch zu einem großen Problem, wenn Sie die gesamte Karte durchlaufen müssen, was für eine große Karte lange dauern kann Ein Thread macht das, alle anderen müssen warten, wenn sie etwas einfügen oder nachschlagen wollen.

Das ConcurrentHashMapVerfahren verwendet sehr ausgefeilte Techniken, um die Notwendigkeit einer Synchronisation zu verringern und einen parallelen Lesezugriff durch mehrere Threads ohne Synchronisation zu ermöglichen, und bietet, was noch wichtiger ist, eine Iterator, die keine Synchronisation erfordert, und ermöglicht sogar das Ändern der Karte während der Interaktion (obwohl es keine Garantie dafür gibt, ob oder Es werden keine Elemente zurückgegeben, die während der Iteration eingefügt wurden.

Michael Borgwardt
quelle
4
Das wollte ich jetzt! :) Der nicht synchronisierte Iterator ist nur reine Süße! Danke für die Info! :) (:
Kounavi
Tolle Antwort. Aber bedeutet dies, dass der Thread beim Abrufen nicht die neuesten Updates erhält, da die Reader-Threads nicht synchron sind.
MrA
@ MrA: Fragen Sie nach der ConcurrentHashMap? Und was meinst du mit "Abrufen"?
Michael Borgwardt
4
@ Michael Borgwardt für ConcurrentHashmap für z. Angenommen, es gibt mehrere Threads. Einige von ihnen aktualisieren die Karte und einige erhalten Daten von derselben Karte. Wenn Threads in diesem Szenario versuchen zu lesen, wird garantiert, dass sie die neuesten Daten erhalten, die aktualisiert wurden, da Reader-Threads keine Sperren enthalten müssen.
MrA
35

ConcurrentHashMap wird bevorzugt, wenn Sie es verwenden können - obwohl mindestens Java 5 erforderlich ist.

Es ist so konzipiert, dass es bei Verwendung durch mehrere Threads gut skaliert werden kann. Die Leistung ist möglicherweise geringfügig schlechter, wenn jeweils nur ein Thread auf die Karte zugreift, jedoch erheblich besser, wenn mehrere Threads gleichzeitig auf die Karte zugreifen.

Ich habe einen Blogeintrag gefunden , der eine Tabelle aus dem hervorragenden Buch Java Concurrency In Practice wiedergibt , das ich sehr empfehlen kann.

Collections.synchronizedMap ist nur dann wirklich sinnvoll, wenn Sie eine Karte mit einigen anderen Merkmalen, möglicherweise einer geordneten Karte wie einer TreeMap, abschließen müssen.

Bill Michell
quelle
2
Ja - anscheinend erwähne ich dieses Buch in jeder anderen Antwort, die ich mache!
Bill Michell
@ BillMichell Link ist defekt
@Govinda Schalten Sie Javascript aus, bevor Sie auf den Link zugreifen. Der Blogeintrag ist noch da!
Bill Michell
32

Der Hauptunterschied zwischen diesen beiden ist der ConcurrentHashMap nur ein Teil der Daten gesperrt wird, die aktualisiert werden, während andere Threads auf einen anderen Teil der Daten zugreifen können. Allerdings Collections.synchronizedMap()wird sperren Sie alle die Daten , während die Aktualisierung, die nur andere Threads können auf die Daten zugreifen , wenn die Sperre aufgehoben wird. Wenn es viele Aktualisierungsvorgänge und relativ wenige Lesevorgänge gibt, sollten Sie auswählen ConcurrentHashMap.

Ein weiterer Unterschied besteht darin, dass ConcurrentHashMapdie Reihenfolge der übergebenen Elemente in der Map nicht beibehalten wird. Dies ähnelt dem HashMapSpeichern von Daten. Es gibt keine Garantie dafür, dass die Elementreihenfolge erhalten bleibt. Während Collections.synchronizedMap()wird die Elementreihenfolge der übergebenen Karte beibehalten. Wenn Sie beispielsweise ein TreeMapan übergebenConcurrentHashMap , um die Elemente in dem ConcurrentHashMapkann die gleiche wie die Reihenfolge , in der nicht sein TreeMap, sondern Collections.synchronizedMap()wird die Bestellung erhalten.

Außerdem, ConcurrentHashMap kann garantiert werden, dass kein ConcurrentModificationExceptionWurf ausgelöst wird, während ein Thread die Map aktualisiert und ein anderer Thread den von der Map erhaltenen Iterator durchläuft. Dies Collections.synchronizedMap()ist jedoch nicht garantiert.

Es gibt einen Beitrag, der die Unterschiede dieser beiden und auch der ConcurrentSkipListMap.

PixelsTech
quelle
13

Synchronisierte Karte:

Synchronized Map unterscheidet sich auch nicht wesentlich von Hashtable und bietet eine ähnliche Leistung in gleichzeitigen Java-Programmen. Der einzige Unterschied zwischen Hashtable und SynchronizedMap besteht darin, dass SynchronizedMap kein Legacy ist und Sie jede Map umschließen können, um ihre synchronisierte Version mithilfe der Collections.synchronizedMap () -Methode zu erstellen.

ConcurrentHashMap:

Die ConcurrentHashMap-Klasse bietet eine gleichzeitige Version der Standard-HashMap. Dies ist eine Verbesserung der synchronizedMap-Funktionalität, die in der Collections-Klasse bereitgestellt wird.

Im Gegensatz zu Hashtable und Synchronized Map wird niemals die gesamte Karte gesperrt, sondern die Karte wird in Segmente unterteilt, und diese werden gesperrt. Es ist besser, wenn die Anzahl der Reader-Threads größer ist als die Anzahl der Writer-Threads.

ConcurrentHashMap ist standardmäßig in 16 Regionen unterteilt und Sperren werden angewendet. Diese Standardnummer kann beim Initialisieren einer ConcurrentHashMap-Instanz festgelegt werden. Beim Einstellen von Daten in einem bestimmten Segment wird die Sperre für dieses Segment erhalten. Dies bedeutet, dass zwei Aktualisierungen immer noch gleichzeitig sicher ausgeführt werden können, wenn sie sich jeweils auf separate Buckets auswirken. Dadurch werden Sperrenkonflikte minimiert und die Leistung maximiert.

ConcurrentHashMap löst keine ConcurrentModificationException aus

ConcurrentHashMap löst keine ConcurrentModificationException aus, wenn ein Thread versucht, sie zu ändern, während ein anderer darüber iteriert

Unterschied zwischen synchornizedMap und ConcurrentHashMap

Collections.synchornizedMap (HashMap) gibt eine Sammlung zurück, die fast Hashtable entspricht, wobei jeder Änderungsvorgang in Map für das Map-Objekt gesperrt ist, während im Fall von ConcurrentHashMap die Thread-Sicherheit erreicht wird, indem die gesamte Map je nach Parallelitätsstufe in verschiedene Partitionen aufgeteilt wird und nur einen bestimmten Teil sperren, anstatt die gesamte Karte zu sperren.

ConcurrentHashMap erlaubt keine Nullschlüssel oder Nullwerte, während synchronisierte HashMap einen Nullschlüssel zulässt.

Ähnliche Links

Link1

Link2

Leistungsvergleich

Ramesh Papaganti
quelle
12

Wie üblich gibt es Kompromisse zwischen Parallelität und Overhead-Geschwindigkeit. Sie müssen wirklich die detaillierten Anforderungen an die Parallelität Ihrer Anwendung berücksichtigen, um eine Entscheidung zu treffen, und dann Ihren Code testen, um festzustellen, ob er gut genug ist.

Zach Scrivena
quelle
12

In ConcurrentHashMapwird die Sperre auf ein Segment anstelle einer gesamten Karte angewendet. Jedes Segment verwaltet seine eigene interne Hash-Tabelle. Die Sperre wird nur für Aktualisierungsvorgänge angewendet. Collections.synchronizedMap(Map)synchronisiert die gesamte Karte.

Satish
quelle
Können Sie bitte einen Blick auf stackoverflow.com/questions/48579060/… werfen ?
gstackoverflow
9

Sie haben Recht HashTable, Sie können es vergessen.

In Ihrem Artikel wird die Tatsache erwähnt, dass HashTable und die synchronisierte Wrapper-Klasse zwar grundlegende Thread-Sicherheit bieten, indem jeweils nur ein Thread auf die Map zugreifen kann. Dies ist jedoch keine "echte" Thread-Sicherheit, da für viele zusammengesetzte Vorgänge immer noch eine zusätzliche Synchronisierung erforderlich ist Beispiel:

synchronized (records) {
  Record rec = records.get(id);
  if (rec == null) {
      rec = new Record(id);
      records.put(id, rec);
  }
  return rec;
}

Denken Sie jedoch nicht, dass dies ConcurrentHashMapeine einfache Alternative für einen HashMapmit einem typischen synchronizedBlock ist, wie oben gezeigt. Lesen Sie diesen Artikel, um die Feinheiten besser zu verstehen.

Eljenso
quelle
7

Hier sind einige:

1) ConcurrentHashMap sperrt nur einen Teil der Karte, aber SynchronizedMap sperrt den gesamten MAp.
2) ConcurrentHashMap bietet eine bessere Leistung als SynchronizedMap und ist skalierbarer.
3) Bei mehreren Lesern und Einzelschreibern ist ConcurrentHashMap die beste Wahl.

Dieser Text stammt aus dem Unterschied zwischen ConcurrentHashMap und Hashtable in Java

Raj
quelle
7

Wir können Thread-Sicherheit erreichen, indem wir ConcurrentHashMap und synchronizedHashmap und Hashtable verwenden. Aber es gibt einen großen Unterschied, wenn man sich die Architektur ansieht.

  1. synchronizedHashmap und Hashtable

Beide behalten die Sperre auf Objektebene bei. Wenn Sie also eine Operation wie put / get ausführen möchten, müssen Sie zuerst die Sperre erwerben. Gleichzeitig dürfen andere Threads keine Operation ausführen. Es kann also immer nur ein Thread damit arbeiten. Die Wartezeit wird hier also länger. Wir können sagen, dass die Leistung im Vergleich zu ConcurrentHashMap relativ niedrig ist.

  1. ConcurrentHashMap

Die Sperre bleibt auf Segmentebene. Es hat 16 Segmente und behält die Parallelitätsstufe standardmäßig als 16 bei. Es können also jeweils 16 Threads auf ConcurrentHashMap ausgeführt werden. Darüber hinaus erfordert der Lesevorgang keine Sperre. So können beliebig viele Threads eine Get-Operation ausführen.

Wenn Thread1 eine Put-Operation in Segment 2 ausführen möchte und Thread2 eine Put-Operation in Segment 4 ausführen möchte, ist dies hier zulässig. Das heißt, 16 Threads können gleichzeitig eine Aktualisierungsoperation (Put / Delete) für ConcurrentHashMap ausführen.

Damit die Wartezeit hier kürzer wird. Daher ist die Leistung relativ besser als bei synchronisierter Hashmap und Hashtable.

Sumanth Varada
quelle
1
, 1. Was passiert, wenn mehrere Threads versuchen, denselben Block zu bearbeiten? 2. Was passiert, wenn beispielsweise zwei Threads versuchen, Daten aus demselben Block zu lesen, während ein anderer Thread gleichzeitig Daten schreibt?
Prnjn
6

ConcurrentHashMap

  • Sie sollten ConcurrentHashMap verwenden, wenn Sie in Ihrem Projekt eine sehr hohe Parallelität benötigen.
  • Es ist threadsicher, ohne die gesamte Karte zu synchronisieren.
  • Das Lesen kann sehr schnell erfolgen, während das Schreiben mit einer Sperre erfolgt.
  • Auf Objektebene gibt es keine Sperrung.
  • Die Verriegelung ist auf Hashmap-Bucket-Ebene viel feiner granular.
  • ConcurrentHashMap löst keine ConcurrentModificationException aus, wenn ein Thread versucht, sie zu ändern, während ein anderer darüber iteriert.
  • ConcurrentHashMap verwendet eine Vielzahl von Sperren.

SynchronizedHashMap

  • Synchronisation auf Objektebene.
  • Jede Lese- / Schreiboperation muss eine Sperre erhalten.
  • Das Sperren der gesamten Sammlung ist ein Leistungsaufwand.
  • Dies ermöglicht im Wesentlichen den Zugriff auf nur einen Thread auf die gesamte Map und blockiert alle anderen Threads.
  • Dies kann zu Konflikten führen.
  • SynchronizedHashMap gibt Iterator zurück, der bei gleichzeitiger Änderung schnell ausfällt.

Quelle

Premraj
quelle
4

ConcurrentHashMap ist für den gleichzeitigen Zugriff optimiert.

Zugriffe sperren nicht die gesamte Karte, sondern verwenden eine feinkörnigere Strategie, die die Skalierbarkeit verbessert. Es gibt auch funktionale Verbesserungen speziell für den gleichzeitigen Zugriff, z. B. gleichzeitige Iteratoren.

Sternenblau
quelle
4

Es gibt eine wichtige Funktion , die Sie beachten sollten, ConcurrentHashMapaußer der von ihr bereitgestellten Parallelitätsfunktion, nämlich den ausfallsicheren Iterator. Ich habe Entwickler gesehen, die ConcurrentHashMapnur verwendet haben, weil sie das Entryset bearbeiten möchten - setzen / entfernen, während sie darüber iterieren. Collections.synchronizedMap(Map)bietet keinen ausfallsicheren Iterator, sondern einen ausfallsicheren Iterator. Fail-Fast-Iteratoren verwenden einen Schnappschuss der Kartengröße, der während der Iteration nicht bearbeitet werden kann.

hi.nitish
quelle
3
  1. Wenn Datenkonsistenz sehr wichtig ist - Verwenden Sie Hashtable oder Collections.synchronizedMap (Map).
  2. Wenn Geschwindigkeit / Leistung sehr wichtig sind und die Datenaktualisierung beeinträchtigt werden kann, verwenden Sie ConcurrentHashMap.
Shivam Maharshi
quelle
2

Wenn Sie das verwenden möchten, ConcurrentHashMapstellen Sie im Allgemeinen sicher, dass Sie bereit sind, Aktualisierungen zu verpassen
(dh das Drucken von Inhalten der HashMap stellt nicht sicher, dass die aktuelle Karte gedruckt wird), und verwenden Sie APIs CyclicBarrier, um die Konsistenz über alle Programme hinweg sicherzustellen Lebenszyklus.

Kounavi
quelle
1

Die Collections.synchronizedMap () -Methode synchronisiert alle Methoden der HashMap und reduziert sie effektiv auf eine Datenstruktur, in die jeweils ein Thread eintreten kann, da jede Methode für eine gemeinsame Sperre gesperrt wird.

In ConcurrentHashMap erfolgt die Synchronisation etwas anders. Anstatt jede Methode für eine gemeinsame Sperre zu sperren, verwendet ConcurrentHashMap eine separate Sperre für separate Buckets, wodurch nur ein Teil der Karte gesperrt wird. Standardmäßig gibt es 16 Eimer und separate Schlösser für separate Eimer. Die Standard-Parallelitätsstufe ist also 16. Das bedeutet, dass theoretisch zu jeder Zeit 16 Threads auf ConcurrentHashMap zugreifen können, wenn sie alle Buckets trennen.

infoj
quelle
1

ConcurrentHashMap wurde als Alternative zu Hashtable in Java 1.5 als Teil des Parallelitätspakets vorgestellt. Mit ConcurrentHashMap haben Sie eine bessere Wahl, nicht nur, wenn es sicher in der gleichzeitigen Multithread-Umgebung verwendet werden kann, sondern auch eine bessere Leistung als Hashtable und synchronizedMap bietet. ConcurrentHashMap bietet eine bessere Leistung, da ein Teil von Map gesperrt wird. Es ermöglicht gleichzeitige Lesevorgänge und behält gleichzeitig die Integrität bei, indem Schreibvorgänge synchronisiert werden.

Wie ConcurrentHashMap implementiert wird

ConcurrentHashMap wurde als Alternative zu Hashtable entwickelt und unterstützt alle Funktionen von Hashtable mit zusätzlichen Funktionen, der sogenannten Parallelitätsstufe. Mit ConcurrentHashMap können mehrere Leser gleichzeitig lesen, ohne Blöcke zu verwenden. Dies wird möglich, indem Map in verschiedene Teile aufgeteilt und nur ein Teil der Map in Updates blockiert wird. Standardmäßig ist die Parallelitätsstufe 16, sodass Map auf 16 Teile aufgeteilt wird und jeder Teil von einem separaten Block verwaltet wird. Dies bedeutet, dass 16 Threads gleichzeitig mit Map arbeiten können, wenn sie mit verschiedenen Teilen von Map arbeiten. Dies macht ConcurrentHashMap sehr produktiv und beeinträchtigt nicht die Thread-Sicherheit.

Wenn Sie an einigen wichtigen Funktionen von ConcurrentHashMap interessiert sind und wenn Sie diese Realisierung von Map verwenden sollten, habe ich nur einen Link zu einem guten Artikel eingefügt - So verwenden Sie ConcurrentHashMap in Java

Oleg Poltoratskii
quelle
0

Neben dem, was vorgeschlagen wurde, möchte ich den Quellcode veröffentlichen, der sich auf bezieht SynchronizedMap .

Um einen MapThread sicher zu machen , können wir verwendenCollections.synchronizedMap Anweisung verwenden und die Map-Instanz als Parameter eingeben.

Die Implementierung von synchronizedMapin Collectionsist wie folgt

   public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
    }

Wie Sie sehen können, wird das Eingabeobjekt vom MapObjekt umschlossen SynchronizedMap.
Lassen Sie uns in die Implementierung von SynchronizedMap,

 private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }

        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }

        private transient Set<K> keySet;
        private transient Set<Map.Entry<K,V>> entrySet;
        private transient Collection<V> values;

        public Set<K> keySet() {
            synchronized (mutex) {
                if (keySet==null)
                    keySet = new SynchronizedSet<>(m.keySet(), mutex);
                return keySet;
            }
        }

        public Set<Map.Entry<K,V>> entrySet() {
            synchronized (mutex) {
                if (entrySet==null)
                    entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
                return entrySet;
            }
        }

        public Collection<V> values() {
            synchronized (mutex) {
                if (values==null)
                    values = new SynchronizedCollection<>(m.values(), mutex);
                return values;
            }
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return m.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return m.hashCode();}
        }
        public String toString() {
            synchronized (mutex) {return m.toString();}
        }

        // Override default methods in Map
        @Override
        public V getOrDefault(Object k, V defaultValue) {
            synchronized (mutex) {return m.getOrDefault(k, defaultValue);}
        }
        @Override
        public void forEach(BiConsumer<? super K, ? super V> action) {
            synchronized (mutex) {m.forEach(action);}
        }
        @Override
        public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
            synchronized (mutex) {m.replaceAll(function);}
        }
        @Override
        public V putIfAbsent(K key, V value) {
            synchronized (mutex) {return m.putIfAbsent(key, value);}
        }
        @Override
        public boolean remove(Object key, Object value) {
            synchronized (mutex) {return m.remove(key, value);}
        }
        @Override
        public boolean replace(K key, V oldValue, V newValue) {
            synchronized (mutex) {return m.replace(key, oldValue, newValue);}
        }
        @Override
        public V replace(K key, V value) {
            synchronized (mutex) {return m.replace(key, value);}
        }
        @Override
        public V computeIfAbsent(K key,
                Function<? super K, ? extends V> mappingFunction) {
            synchronized (mutex) {return m.computeIfAbsent(key, mappingFunction);}
        }
        @Override
        public V computeIfPresent(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.computeIfPresent(key, remappingFunction);}
        }
        @Override
        public V compute(K key,
                BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.compute(key, remappingFunction);}
        }
        @Override
        public V merge(K key, V value,
                BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
            synchronized (mutex) {return m.merge(key, value, remappingFunction);}
        }

        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

Was SynchronizedMapgetan werden kann, kann als Hinzufügen einer einzelnen Sperre zur primären Methode des Eingabeobjekts zusammengefasst Mapwerden. Auf alle von der Sperre geschützten Methoden können nicht mehrere Threads gleichzeitig zugreifen. Das bedeutet normale Operationen wieput und getgleichzeitig von einem einzelnen Thread für alle Daten in der ausgeführt werden könnenMap Objekt.

Es macht das Map Objekt-Thread jetzt sicher, aber in einigen Szenarien kann die Leistung zu einem Problem werden.

Die ConcurrentMapImplementierung ist weitaus komplizierter. Weitere Informationen finden Sie unter Erstellen einer besseren HashMap . Kurz gesagt, es wird unter Berücksichtigung von Thread-Sicherheit und Leistung implementiert.

Eugene
quelle