ConcurrentHashMap in Java

Die Java ConcurrentHashMap-Klasse ist Teil der Concurrency Collection Klassen. Es handelt sich dabei um eine Hash-Tabelle-Implementierung, die gleichzeitige Abrufe und Aktualisierungen unterstützt. Sie wird in einer Multi-Thread-Umgebung verwendet, um ConcurrentModificationException zu vermeiden.

ConcurrentHashMap

Wenn wir versuchen, die Sammlung zu ändern, während wir sie durchlaufen, erhalten wir eine ConcurrentModificationException. Java 1.5 führte Concurrent-Klassen im java.util.concurrent Paket ein, um dieses Szenario zu überwinden. ConcurrentHashMap ist die Map-Implementierung, die es uns ermöglicht, die Map während der Iteration zu ändern. Die Operationen der ConcurrentHashMap sind Thread-sicher. ConcurrentHashMap erlaubt keine Null-Werte für Schlüssel und Werte.

Beispiel für Java ConcurrentHashMap

Die ConcurrentHashMap-Klasse ähnelt der HashMap, mit dem Unterschied, dass sie Thread-sicher ist und Änderungen während der Iteration zulässt.

package com.journaldev.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {

	public static void main(String[] args) {

		//ConcurrentHashMap
		Map<String,String> myMap = new ConcurrentHashMap<String,String>();
		myMap.put("1", "1");
		myMap.put("2", "1");
		myMap.put("3", "1");
		myMap.put("4", "1");
		myMap.put("5", "1");
		myMap.put("6", "1");
		System.out.println("ConcurrentHashMap before iterator: "+myMap);
		Iterator<String> it = myMap.keySet().iterator();

		while(it.hasNext()){
			String key = it.next();
			if(key.equals("3")) myMap.put(key+"new", "new3");
		}
		System.out.println("ConcurrentHashMap after iterator: "+myMap);

		//HashMap
		myMap = new HashMap<String,String>();
		myMap.put("1", "1");
		myMap.put("2", "1");
		myMap.put("3", "1");
		myMap.put("4", "1");
		myMap.put("5", "1");
		myMap.put("6", "1");
		System.out.println("HashMap before iterator: "+myMap);
		Iterator<String> it1 = myMap.keySet().iterator();

		while(it1.hasNext()){
			String key = it1.next();
			if(key.equals("3")) myMap.put(key+"new", "new3");
		}
		System.out.println("HashMap after iterator: "+myMap);
	}

}

Ausgabe:

ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
	at java.util.HashMap$KeyIterator.next(HashMap.java:828)
	at com.test.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:44)

Es ist klar, dass ConcurrentHashMap sich um den neuen Eintrag in der Map kümmert, während HashMap eine ConcurrentModificationException wirft. Schauen wir uns den Ausnahmestapel genauer an. Die folgende Anweisung hat eine Exception ausgelöst.

String key = it1.next();

Das bedeutet, dass der neue Eintrag in die HashMap eingefügt wurde, aber der Iterator fehlschlägt. Tatsächlich ist der Iterator für Collection-Objekte fail-fast, d.h. jede Änderung in der Struktur oder der Anzahl der Einträge im Collection-Objekt löst die Ausnahme aus.

Wie erfährt der Iterator von der Änderung in der Collection?

Wir haben den Satz der Schlüssel aus der HashMap genommen und iterieren jetzt darüber. HashMap enthält eine Variable, um die Anzahl der Änderungen zu zählen, und der Iterator verwendet sie, wenn Sie die next() Funktion aufrufen, um den nächsten Eintrag zu erhalten. HashMap.java:

/**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).
     */
    transient volatile int modCount;

Ändern wir den Code ein wenig, um aus der Iterator-Schleife auszusteigen, wenn wir den neuen Eintrag einfügen. Alles, was wir tun müssen, ist eine break-Anweisung nach dem put-Aufruf hinzuzufügen.

if(key.equals("3")){
	myMap.put(key+"new", "new3");
	break;
}

Die Ausgabe mit dem obigen Code:

ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
HashMap after iterator: {3=1, 2=1, 1=1, 3new=new3, 6=1, 5=1, 4=1}

Was passiert, wenn der Schlüsselwert geändert wird?

Was passiert, wenn wir keinen neuen Eintrag hinzufügen, sondern das vorhandene Schlüssel-Wert-Paar aktualisieren? Wirft es eine Ausnahme? Lassen Sie uns den Code im Originalprogramm ändern und es überprüfen.

//myMap.put(key+"new", "new3");
myMap.put(key, "new3");

Es wird keine Ausnahme geben, da die Sammlung zwar geändert wird, aber ihre Struktur gleich bleibt.

Weiterführende Lektüre

Haben Sie diese spitzen Klammern bemerkt, als wir unser Sammlungsobjekt und den Iterator erstellt haben? Es nennt sich Generics und ist sehr leistungsstark, wenn es um die Typüberprüfung zur Kompilierzeit geht, um ClassCastException zur Laufzeit zu vermeiden. Erfahren Sie mehr über Generics in Java Generics Beispiel. Sie sollten auch Java-Sammlungsinterviewfragen und Iterator-Entwurfsmuster in Java lesen.

Sie können weitere Java-Sammlungsbeispiele in unserem GitHub-Repository überprüfen.

Referenz: API-Dokumentation

Source:
https://www.digitalocean.com/community/tutorials/concurrenthashmap-in-java