HashMap Vs. ConcurrentHashMap Vs. SynchronizedMap – Bir HashMap Java'da Nasıl Senkronize Edilebilir
Yayınlanan: 2015-01-29
HashMap
, Java'da çok güçlü bir veri yapısıdır. Her gün ve neredeyse tüm uygulamalarda kullanıyoruz. Daha önce Threadsafe önbelleğini nasıl uygulayabilirim, Hashmap'i Arraylist'e nasıl dönüştürebilirim üzerine yazdığım bir kaç örnek var.
Yukarıdaki her iki örnekte de Hashmap kullandık, ancak bunlar Hashmap'in oldukça basit kullanım durumlarıdır. HashMap is a non-synchronized
koleksiyon sınıfıdır.
Aşağıdaki sorulardan herhangi biri var mı?
- ConcurrentHashMap ve Collections.synchronizedMap(Map) arasındaki fark nedir?
- Performans açısından ConcurrentHashMap ve Collections.synchronizedMap(Map) arasındaki fark nedir?
- ConcurrentHashMap ve Collections.synchronizedMap() karşılaştırması
- Popüler HashMap ve ConcurrentHashMap mülakat soruları
Bu derste, yukarıdaki tüm sorguları ve nedenini why and how
ve neden senkronize edebileceğimizi gözden geçireceğiz.
Niye ya?
Map nesnesi, benzersiz bir tanımlama key
ve eşlenmiş bir value
birleşiminden oluşan öğeleri depolayan bir ilişkisel kapsayıcıdır. Farklı iş parçacıklarında anahtar değerini değiştirmek veya okumak isteyebileceğiniz çok yüksek eşzamanlı uygulamanız varsa, Concurrent Hashmap kullanmak idealdir. En iyi örnek, eşzamanlı okuma/yazma işlemlerini gerçekleştiren Üretici Tüketicidir.
Peki iş parçacığı güvenli Harita ne anlama geliyor? Birden multiple threads
aynı anda bir karma haritaya erişirse ve iş parçacıklarından en az biri haritayı yapısal olarak değiştirirse, içeriğin tutarsız bir görünümünden kaçınmak için must be synchronized externally
.
Nasıl?
HashMap'i senkronize etmenin iki yolu var
- Java Koleksiyonları synchronizedMap() yöntemi
- ConcurrentHashMap'i kullanın
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 > ( ) ; |
EşzamanlıHashMap
- Projenizde çok yüksek eşzamanlılığa ihtiyacınız olduğunda ConcurrentHashMap kullanmalısınız.
-
whole map
senkronize etmeden iş parçacığı için güvenlidir. - Yazma bir kilitle yapılırken okumalar çok hızlı gerçekleşebilir.
- Nesne düzeyinde kilitleme yoktur.
- Kilitleme, hashmap kova düzeyinde çok daha ince bir ayrıntı düzeyindedir.
-
ConcurrentModificationException
, bir iş parçacığı onu değiştirmeye çalışırken diğeri üzerinde yineleme yaparken ConcurrentModificationException oluşturmaz. - ConcurrentHashMap çok sayıda kilit kullanır.
SenkronizeHashMap
- Nesne düzeyinde senkronizasyon.
- Her okuma/yazma işleminin kilit alması gerekir.
- Tüm koleksiyonu kilitlemek bir performans yüküdür.
- Bu, esasen tüm haritaya yalnızca bir dizine erişim sağlar ve diğer tüm dizileri engeller.
- Tartışmaya neden olabilir.
- SynchronizedHashMap, eşzamanlı değişiklikte hızlı bir şekilde başarısız olan
Iterator
döndürür.
Şimdi koda bir göz atalım
-
CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
sınıfı oluşturun - Her HashTable, SynchronizedMap ve CrunchifyConcurrentHashMap için nesne oluşturun
- Haritadan 500 bin giriş ekleyin ve alın
- Başlangıç ve bitiş zamanını ölçün ve zamanı milisaniye cinsinden görüntüleyin
-
5 threads
paralel olarak çalıştırmak için ExecutorService kullanacağız
İşte bir Java kodu:
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()
, yürütücü hizmetin daha fazla gelen görevi almadığı anlamına gelir.- Bir kapatma isteğinden sonra
awaitTermination()
çağrılır.
Bu nedenle, önce serviceExecutor'ı kapatmanız ve ardından iş parçacıklarının bitmesini engellemeniz ve beklemeniz gerekir.
Eclipse Konsolu Sonucu:
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 |