Java ConcurrentHashMap-klasse maakt deel uit van de Concurrency Collection Classes. Het is een hash-tabelimplementatie die gelijktijdige ophaling en updates ondersteunt. Het wordt gebruikt in een multi-threaded omgeving om ConcurrentModificationException te vermijden.
ConcurrentHashMap
Als we proberen de collectie te wijzigen tijdens iteratie, krijgen we ConcurrentModificationException
. Java 1.5 introduceerde Concurrent-klassen in het java.util.concurrent
-pakket om dit scenario te overwinnen. ConcurrentHashMap is de Map-implementatie die ons in staat stelt de Map te wijzigen tijdens iteratie. De bewerkingen van ConcurrentHashMap zijn thread-safe. ConcurrentHashMap staat geen null toe voor sleutels en waarden.
Voorbeeld van Java ConcurrentHashMap
De ConcurrentHashMap
-klasse is vergelijkbaar met HashMap, behalve dat het thread-safe is en wijzigingen tijdens iteratie toestaat.
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);
}
}
Output:
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)
Het is duidelijk uit de uitvoer dat ConcurrentHashMap voor de nieuwe invoer in de map zorgt tijdens iteratie, terwijl HashMap een ConcurrentModificationException
genereert. Laten we de uitzonderings-stacktrace nauwkeurig bekijken. De volgende verklaring heeft een uitzondering veroorzaakt.
String key = it1.next();
Het betekent dat de nieuwe invoer is toegevoegd aan de HashMap, maar dat de Iterator faalt. Eigenlijk is de Iterator op Collection-objecten fail-fast, dat wil zeggen dat elke wijziging in de structuur of het aantal items in het collectieobject de uitzondering zal activeren.
Hoe weet de iterator over de wijziging in de collectie?
We hebben de set sleutels uit de HashMap genomen en itereren er vervolgens over. HashMap bevat een variabele om het aantal wijzigingen bij te houden, en de iterator gebruikt deze wanneer je de next() functie aanroept om de volgende invoer te krijgen. 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;
Laten we de code een beetje aanpassen om uit de iterator-lus te komen wanneer we de nieuwe invoer invoegen. Alles wat we moeten doen, is een break statement toevoegen na de put-aanroep.
if(key.equals("3")){
myMap.put(key+"new", "new3");
break;
}
De uitvoer met de bovenstaande 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}
Wat gebeurt er als de sleutelwaarde wordt gewijzigd?
Wat als we geen nieuwe invoer toevoegen maar de bestaande sleutel-waarde pair bijwerken? Zal het een uitzondering genereren? Laten we de code in het oorspronkelijke programma wijzigen en het controleren.
//myMap.put(key+"new", "new3");
myMap.put(key, "new3");
Er zal geen uitzondering optreden omdat de verzameling is gewijzigd maar de structuur ervan hetzelfde blijft.
Verder lezen
Heb je die hoekige haken opgemerkt bij het maken van ons verzamelobject en Iterator? Het heet generics en het is zeer krachtig als het gaat om typecontrole op compileertijd om ClassCastException bij runtime te voorkomen. Lees meer over generics in Java Generics Example. Je zou ook moeten lezen Java Collections Interview Questions en Iterator Design Pattern in Java.
Je kunt meer Java-collectievoorbeelden bekijken op onze GitHub Repository.
Referentie: API Doc
Source:
https://www.digitalocean.com/community/tutorials/concurrenthashmap-in-java