HashMap เทียบกับ เทียบกับ HashMap พร้อมกัน SynchronizedMap - HashMap สามารถซิงโครไนซ์ใน Java . ได้อย่างไร
เผยแพร่แล้ว: 2015-01-29
HashMap
เป็นโครงสร้างข้อมูลที่ทรงพลังมากใน Java เราใช้ทุกวันและแทบทุกแอปพลิเคชัน มีตัวอย่างบางส่วนที่ฉันได้เขียนไว้ก่อนหน้านี้เกี่ยวกับวิธีการใช้งานแคช Threadsafe, วิธีแปลง Hashmap เป็น Arraylist?
เราใช้ Hashmap ในทั้งสองตัวอย่างด้านบน แต่สิ่งเหล่านี้เป็นกรณีการใช้งานที่ค่อนข้างง่ายของ Hashmap HashMap is a non-synchronized
คุณมีคำถามด้านล่างหรือไม่?
- ConcurrentHashMap และ Collections.synchronizedMap (แผนที่) แตกต่างกันอย่างไร
- ConcurrentHashMap และ Collections.synchronizedMap (Map) แตกต่างกันอย่างไรในแง่ของประสิทธิภาพ
- ConcurrentHashMap กับ Collections.synchronizedMap ()
- คำถามสัมภาษณ์ HashMap และ ConcurrentHashMap ยอดนิยม
ในบทช่วยสอนนี้ เราจะอธิบายคำถามข้างต้นทั้งหมดและให้เหตุผล why and how
เราจะซิงโครไนซ์ Hashmap ได้อย่างไร
ทำไม?
ออบเจ็กต์ Map เป็นคอนเทนเนอร์ที่เชื่อมโยงกันซึ่งเก็บองค์ประกอบ ซึ่งเกิดขึ้นจากการรวมกันของ key
ระบุเฉพาะและ value
ที่แมป หากคุณมีแอปพลิเคชันที่ทำงานพร้อมกันอย่างมากซึ่งคุณอาจต้องการแก้ไขหรืออ่านค่าคีย์ในเธรดต่างๆ กัน ขอแนะนำให้ใช้ Concurrent Hashmap ตัวอย่างที่ดีที่สุดคือ Producer Consumer ซึ่งจัดการการอ่าน/เขียนพร้อมกัน
แล้ว Thread-safe Map หมายความว่าอย่างไร? หาก multiple threads
เข้าถึงแม็พแฮชพร้อมกัน และเธรดอย่างน้อยหนึ่งเธรดแก้ไขโครงสร้างแม็พ จะ must be synchronized externally
เพื่อหลีกเลี่ยงมุมมองเนื้อหาที่ไม่สอดคล้องกัน
ยังไง?
มีสองวิธีที่เราสามารถซิงโครไนซ์ HashMap
- Java Collections synchronizedMap() method
- ใช้ 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,000 รายการจาก Map
- วัดเวลาเริ่มต้นและสิ้นสุดและแสดงเวลาเป็นมิลลิวินาที
- เราจะใช้ ExecutorService เพื่อรัน
5 threads
แบบขนาน
นี่คือรหัส 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()
หมายความว่าบริการตัวดำเนินการจะไม่รับงานที่เข้ามาอีก-
awaitTermination()
ถูกเรียกใช้หลังจากคำขอปิดระบบ
ดังนั้น คุณต้องปิด serviceExecutor ก่อน จากนั้นจึงบล็อกและรอให้เธรดเสร็จสิ้น
ผลลัพธ์ของคอนโซล 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 |