HashMap vs. ConcurrentHashMap Vs. SynchronizedMap – Wie eine HashMap in Java synchronisiert werden kann
Veröffentlicht: 2015-01-29
HashMap
ist eine sehr mächtige Datenstruktur in Java. Wir verwenden es täglich und in fast allen Anwendungen. Es gibt einige Beispiele, die ich zuvor geschrieben habe, wie man Threadsafe-Cache implementiert, wie man Hashmap in Arraylist konvertiert?
Wir haben Hashmap in beiden obigen Beispielen verwendet, aber das sind ziemlich einfache Anwendungsfälle von Hashmap. HashMap is a non-synchronized
Sammlungsklasse.
Haben Sie eine der folgenden Fragen?
- Was ist der Unterschied zwischen ConcurrentHashMap und Collections.synchronizedMap(Map)?
- Was ist der Unterschied zwischen ConcurrentHashMap und Collections.synchronizedMap(Map) in Bezug auf die Leistung?
- ConcurrentHashMap vs. Collections.synchronizedMap()
- Beliebte HashMap- und ConcurrentHashMap-Interviewfragen
In diesem Tutorial werden wir alle oben genannten Abfragen durchgehen und begründen, why and how
wir Hashmap synchronisieren können.
Warum?
Das Map-Objekt ist ein assoziativer Container, der Elemente speichert, die durch eine Kombination aus einem eindeutig identifizierenden key
und einem zugeordneten value
gebildet werden. Wenn Sie eine sehr parallele Anwendung haben, in der Sie Schlüsselwerte in verschiedenen Threads ändern oder lesen möchten, ist es ideal, Concurrent Hashmap zu verwenden. Bestes Beispiel ist Producer Consumer, der gleichzeitiges Lesen/Schreiben verarbeitet.
Was bedeutet also die Thread-sichere Karte? Wenn multiple threads
gleichzeitig auf eine Hash-Map zugreifen und mindestens einer der Threads die Map strukturell verändert, must be synchronized externally
, um eine inkonsistente Ansicht der Inhalte zu vermeiden.
Wie?
Es gibt zwei Möglichkeiten, HashMap zu synchronisieren
- SynchronizedMap()-Methode von Java-Sammlungen
- Verwenden Sie ConcurrentHashMap
1 2 3 4 5 6 7 8 |
//Hashtable Map < String , String > normalMap = new Hashtable < String , String > ( ) ; //synchronizedMap synchronizedHashMap = Collections . synchronizedMap ( new HashMap < String , String > ( ) ) ; //ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap < String , String > ( ) ; |
ConcurrentHashMap
- Sie sollten ConcurrentHashMap verwenden, wenn Sie in Ihrem Projekt eine sehr hohe Parallelität benötigen.
- Es ist Thread-sicher, ohne die
whole map
zu synchronisieren. - Das Lesen kann sehr schnell erfolgen, während das Schreiben mit einer Sperre erfolgt.
- Es gibt keine Sperre auf Objektebene.
- Die Sperrung erfolgt auf Hashmap-Bucket-Ebene mit einer viel feineren Granularität.
- 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.
- Jeder Lese-/Schreibvorgang muss eine Sperre erwerben.
- Das Sperren der gesamten Sammlung ist ein Leistungsmehraufwand.
- Dies gibt im Wesentlichen nur einem Thread Zugriff auf die gesamte Karte und blockiert alle anderen Threads.
- Es kann zu Konflikten kommen.
- SynchronizedHashMap gibt
Iterator
zurück, der bei gleichzeitiger Änderung schnell fehlschlägt.
Schauen wir uns nun den Code an
- Erstellen Sie die Klasse
CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
- Erstellen Sie ein Objekt für jede HashTable, SynchronizedMap und CrunchifyConcurrentHashMap
- Fügen Sie 500.000 Einträge von Map hinzu und rufen Sie sie ab
- Start- und Endzeit messen und Zeit in Millisekunden anzeigen
- Wir werden ExecutorService verwenden, um
5 threads
parallel auszuführen
Hier ist ein Java-Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package crunchify . com . tutorials ; import java . util . Collections ; import java . util . HashMap ; import java . util . Hashtable ; import java . util . Map ; import java . util . concurrent . ConcurrentHashMap ; import java . util . concurrent . ExecutorService ; import java . util . concurrent . Executors ; import java . util . concurrent . TimeUnit ; /** * @author Crunchify.com * */ public class CrunchifyConcurrentHashMapVsSynchronizedMap { public final static int THREAD_POOL_SIZE = 5 ; public static Map < String , Integer > crunchifyHashTableObject = null ; public static Map < String , Integer > crunchifySynchronizedMapObject = null ; public static Map < String , Integer > crunchifyConcurrentHashMapObject = null ; public static void main ( String [ ] args ) throws InterruptedException { // Test with Hashtable Object crunchifyHashTableObject = new Hashtable < String , Integer > ( ) ; crunchifyPerformTest ( crunchifyHashTableObject ) ; // Test with synchronizedMap Object crunchifySynchronizedMapObject = Collections . synchronizedMap ( new HashMap < String , Integer > ( ) ) ; crunchifyPerformTest ( crunchifySynchronizedMapObject ) ; // Test with ConcurrentHashMap Object crunchifyConcurrentHashMapObject = new ConcurrentHashMap < String , Integer > ( ) ; crunchifyPerformTest ( crunchifyConcurrentHashMapObject ) ; } public static void crunchifyPerformTest ( final Map < String , Integer > crunchifyThreads ) throws InterruptedException { System . out . println ( "Test started for: " + crunchifyThreads . getClass ( ) ) ; long averageTime = 0 ; for ( int i = 0 ; i < 5 ; i ++ ) { long startTime = System . nanoTime ( ) ; ExecutorService crunchifyExServer = Executors . newFixedThreadPool ( THREAD_POOL_SIZE ) ; for ( int j = 0 ; j < THREAD_POOL_SIZE ; j ++ ) { crunchifyExServer . execute ( new Runnable ( ) { @SuppressWarnings ( "unused" ) @Override public void run ( ) { for ( int i = 0 ; i < 500000 ; i ++ ) { Integer crunchifyRandomNumber = ( int ) Math . ceil ( Math . random ( ) * 550000 ) ; // Retrieve value. We are not using it anywhere Integer crunchifyValue = crunchifyThreads . get ( String . valueOf ( crunchifyRandomNumber ) ) ; // Put value crunchifyThreads . put ( String . valueOf ( crunchifyRandomNumber ) , crunchifyRandomNumber ) ; } } } ) ; } // Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation // has no additional effect if already shut down. // This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that. crunchifyExServer . shutdown ( ) ; // Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is // interrupted, whichever happens first. crunchifyExServer . awaitTermination ( Long . MAX_VALUE , TimeUnit . DAYS ) ; long entTime = System . nanoTime ( ) ; long totalTime = ( entTime - startTime ) / 1000000L ; averageTime += totalTime ; System . out . println ( "500K entried added/retrieved in " + totalTime + " ms" ) ; } System . out . println ( "For " + crunchifyThreads . getClass ( ) + " the average time is " + averageTime / 5 + " ms\n" ) ; } } |

shutdown()
bedeutet, dass der Executor-Dienst keine eingehenden Aufgaben mehr annimmt.-
awaitTermination()
wird nach einer Anforderung zum Herunterfahren aufgerufen.
Daher müssen Sie zuerst den serviceExecutor herunterfahren und dann blockieren und warten, bis die Threads beendet sind.
Ergebnis der Eclipse-Konsole:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Test started for: class java.util.Hashtable 500K entried added/retrieved in 1832 ms 500K entried added/retrieved in 1723 ms 500K entried added/retrieved in 1782 ms 500K entried added/retrieved in 1607 ms 500K entried added/retrieved in 1851 ms For class java.util.Hashtable the average time is 1759 ms Test started for: class java.util.Collections$SynchronizedMap 500K entried added/retrieved in 1923 ms 500K entried added/retrieved in 2032 ms 500K entried added/retrieved in 1621 ms 500K entried added/retrieved in 1833 ms 500K entried added/retrieved in 2048 ms For class java.util.Collections$SynchronizedMap the average time is 1891 ms Test started for: class java.util.concurrent.ConcurrentHashMap 500K entried added/retrieved in 1068 ms 500K entried added/retrieved in 1029 ms 500K entried added/retrieved in 1165 ms 500K entried added/retrieved in 840 ms 500K entried added/retrieved in 1017 ms For class java.util.concurrent.ConcurrentHashMap the average time is 1023 ms |