HashMap vs. HashMap simultanei vs. SynchronizedMap – Come sincronizzare una HashMap in Java
Pubblicato: 2015-01-29
HashMap
è una struttura dati molto potente in Java. Lo usiamo tutti i giorni e quasi in tutte le applicazioni. Ci sono alcuni esempi che ho scritto prima su Come implementare la cache Threadsafe, Come convertire Hashmap in Arraylist?
Abbiamo usato Hashmap in entrambi gli esempi precedenti, ma quelli sono casi d'uso piuttosto semplici di Hashmap. HashMap is a non-synchronized
.
Hai una delle seguenti domande?
- Qual è la differenza tra ConcurrentHashMap e Collections.synchronizedMap(Map)?
- Qual è la differenza tra ConcurrentHashMap e Collections.synchronizedMap(Map) in termini di prestazioni?
- ConcurrentHashMap vs Collections.synchronizedMap()
- Domande popolari dell'intervista su HashMap e ConcurrentHashMap
In questo tutorial esamineremo tutte le domande di cui sopra e il motivo why and how
potremmo sincronizzare Hashmap?
Come mai?
L'oggetto Mappa è un contenitore associativo che memorizza elementi, formato da una combinazione di una key
di identificazione univoca e un value
mappato. Se disponi di un'applicazione molto simultanea in cui potresti voler modificare o leggere il valore della chiave in thread diversi, è l'ideale utilizzare Hashmap simultaneo. Il miglior esempio è Producer Consumer che gestisce la lettura/scrittura simultanea.
Quindi cosa significa la mappa thread-safe? Se multiple threads
accedono a una mappa hash contemporaneamente e almeno uno dei thread modifica strutturalmente la mappa, must be synchronized externally
per evitare una visualizzazione incoerente dei contenuti.
Come?
Ci sono due modi per sincronizzare HashMap
- Raccolte Java metodo sincronizzatoMap()
- Usa 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 > ( ) ; |
HashMap simultaneo
- Dovresti usare ConcurrentHashMap quando hai bisogno di una concorrenza molto alta nel tuo progetto.
- È thread-safe senza sincronizzare l'
whole map
. - Le letture possono avvenire molto velocemente mentre la scrittura viene eseguita con un blocco.
- Non vi è alcun blocco a livello di oggetto.
- Il blocco ha una granularità molto più fine a livello di bucket hashmap.
- ConcurrentHashMap non genera
ConcurrentModificationException
se un thread tenta di modificarlo mentre un altro sta iterando su di esso. - ConcurrentHashMap utilizza una moltitudine di blocchi.
Sincronizzato HashMap
- Sincronizzazione a livello di oggetto.
- Ogni operazione di lettura/scrittura deve acquisire il blocco.
- Il blocco dell'intera raccolta è un sovraccarico di prestazioni.
- Questo essenzialmente dà accesso a un solo thread all'intera mappa e blocca tutti gli altri thread.
- Potrebbe causare contese.
- SynchronizedHashMap restituisce
Iterator
, che fallisce rapidamente in caso di modifiche simultanee.
Ora diamo un'occhiata al codice
- Crea classe
CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
- Crea un oggetto per ogni HashTable, SynchronizedMap e CrunchifyConcurrentHashMap
- Aggiungi e recupera 500.000 voci dalla mappa
- Misura l'ora di inizio e di fine e visualizza il tempo in millisecondi
- Useremo ExecutorService per eseguire
5 threads
in parallelo
Ecco un codice Java:
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()
significa che il servizio executor non richiede più attività in arrivo.-
awaitTermination()
viene richiamato dopo una richiesta di arresto.
E quindi, devi prima arrestare serviceExecutor e quindi bloccare e attendere che i thread finiscano.
Risultato console Eclipse:
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 |