HashMap مقابل. ConcurrentHashMap مقابل. SynchronizedMap - كيف يمكن مزامنة HashMap في Java
نشرت: 2015-01-29
HashMap
هي بنية بيانات قوية جدًا في Java. نستخدمه كل يوم وتقريباً في جميع التطبيقات. هناك عدد قليل جدًا من الأمثلة التي كتبتها من قبل حول كيفية تنفيذ ذاكرة التخزين المؤقت لـ Threadafe ، وكيفية تحويل Hashmap إلى Arraylist؟
استخدمنا Hashmap في كلا المثالين أعلاه ، لكن هذه حالات استخدام بسيطة جدًا لـ Hashmap. HashMap is a non-synchronized
.
هل لديك أي من الأسئلة أدناه؟
- ما الفرق بين ConcurrentHashMap و Collections.synchronizedMap (Map)؟
- ما الفرق بين ConcurrentHashMap و Collections.synchronizedMap (Map) من حيث الأداء؟
- ConcurrentHashMap مقابل Collections.synchronizedMap ()
- أسئلة مقابلة HashMap الشائعة و ConcurrentHashMap
في هذا البرنامج التعليمي سوف نتجاوز جميع الاستعلامات المذكورة أعلاه والسبب في ذلك why and how
يمكننا مزامنة Hashmap؟
لماذا ا؟
كائن الخريطة عبارة عن حاويات ارتباطية تخزن العناصر ، وتتكون من مجموعة من key
تعريف فريد value
معينة. إذا كان لديك تطبيق متزامن للغاية قد ترغب في تعديل أو قراءة قيمة المفتاح في سلاسل مختلفة ، فمن المثالي استخدام Concurrent Hashmap. أفضل مثال على ذلك هو Producer Consumer الذي يتعامل مع القراءة / الكتابة المتزامنة.
إذن ماذا تعني خريطة خيط آمن؟ في حالة وصول multiple threads
إلى خريطة تجزئة بشكل متزامن ، ويقوم واحد على الأقل من الخيوط بتعديل الخريطة هيكليًا ، must be synchronized externally
لتجنب عرض المحتويات غير المتناسق.
كيف؟
هناك طريقتان يمكننا من خلالهما مزامنة HashMap
- طريقة synizedMap () لمجموعات Java
- استخدم 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
- يجب عليك استخدام ConcurrentHashMap عندما تحتاج إلى تزامن عالٍ جدًا في مشروعك.
- إنه مؤشر ترابط آمن دون مزامنة
whole map
. - يمكن أن تحدث القراءات بسرعة كبيرة أثناء إجراء الكتابة بقفل.
- لا يوجد قفل على مستوى الكائن.
- يكون القفل في مستوى حبيبات أدق بكثير على مستوى دلو التجزئة.
- لا يقوم ConcurrentHashMap بإلقاء
ConcurrentModificationException
إذا حاول أحد الخيوط تعديله بينما يقوم مؤشر ترابط آخر بالتكرار عليه. - يستخدم ConcurrentHashMap العديد من الأقفال.
SynchronizedHashMap
- التزامن على مستوى الكائن.
- تحتاج كل عملية قراءة / كتابة إلى الحصول على قفل.
- يعد تأمين المجموعة بأكملها بمثابة عبء في الأداء.
- يتيح هذا بشكل أساسي الوصول إلى مؤشر ترابط واحد فقط للخريطة بأكملها ويحظر جميع سلاسل الرسائل الأخرى.
- قد يسبب الخلاف.
- يُعيد SynchronizedHashMap
Iterator
، الذي يفشل بسرعة في التعديل المتزامن.
الآن دعونا نلقي نظرة على الكود
- قم بإنشاء فئة
CrunchifyConcurrentHashMapVsSynchronizedHashMap.java
- إنشاء كائن لكل HashTable و SynchronizedMap و CrunchifyConcurrentHashMap
- قم بإضافة واسترداد 500 ألف إدخال من الخريطة
- قم بقياس وقت البدء والانتهاء ووقت العرض بالمللي ثانية
- سوف نستخدم ExecutorService لتشغيل
5 threads
ترابط بالتوازي
هنا كود جافا:
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()
أن خدمة المنفذ لا تستغرق المزيد من المهام الواردة. - تم استدعاء
awaitTermination()
بعد طلب إيقاف التشغيل.
وبالتالي ، تحتاج أولاً إلى إيقاف تشغيل serviceExecutor ثم حظر وانتظار سلاسل الرسائل حتى تنتهي.
نتيجة وحدة التحكم في الكسوف:
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 |