Java中ConcurrentHashMap是如何實現線程安全
ConcurrentHashMap是一個哈希表,支持檢索的全並發和更新的高預期並發。此類遵循與 Hashtable 相同的功能規范,並包含 Hashtable 的所有方法。ConcurrentHashMap 位於 java.util.Concurrent 包中。
語法:
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable
其中 K 指的是這個映射所維護的鍵的類型,V 指的是映射值的類型
ConcurrentHashmap 的需要:
- HashMap雖然有很多優點,但不能用於多線程,因為它不是線程安全的。
- 盡管 Hashtable 被認為是線程安全的,但它也有一些缺點。例如,Hashtable 需要鎖定才能讀取打開,即使它不影響對象。
- n HashMap,如果一個線程正在迭代一個對象,另一個線程試圖訪問同一個對象,它會拋出 ConcurrentModificationException,而並發 hashmap 不會拋出 ConcurrentModificationException。
如何使 ConcurrentHashMap 線程安全成為可能?
- java.util.Concurrent.ConcurrentHashMap類通過將map劃分為segment來實現線程安全,不是整個對象需要鎖,而是一個segment,即一個線程需要一個segment的鎖。
- 在 ConcurrenHashap 中,讀操作不需要任何鎖。
示例 1:
import java.util.*; import java.util.concurrent.*; // 擴展Thread類的主類 class GFG extends Thread { // 創建靜態 HashMap 類對象 static HashMap m = new HashMap(); public void run() { // try 塊檢查異常 try { // 讓線程休眠 3 秒 Thread.sleep(2000); } catch (InterruptedException e) { } System.out.println("子線程更新映射"); m.put(103, "C"); } public static void main(String arg[]) throws InterruptedException { m.put(101, "A"); m.put(102, "B"); GFG t = new GFG(); t.start(); Set s1 = m.keySet(); Iterator itr = s1.iterator(); while (itr.hasNext()) { Integer I1 = (Integer)itr.next(); System.out.println( "主線程迭代映射和當前條目是:" + I1 + "..." + m.get(I1)); Thread.sleep(3000); } System.out.println(m); } }
輸出:
主線程迭代映射和當前條目是:101…A
子線程更新映射
Exception in thread “main” java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1516)
at Main.main(Main.java:30)
輸出說明:
上述程序中使用的類擴展瞭 Thread 類。讓我們看看控制流。所以,最初,上面的java程序包含一個線程。當我們遇到語句 Main t= new Main() 時,我們正在為擴展 Thread 類的類創建一個對象。因此,每當我們調用 t.start() 方法時,子線程都會被激活並調用 run() 方法. 現在主線程開始執行,每當子線程更新同一個地圖對象時,都會拋出一個名為 ConcurrentModificationException 的異常。
現在讓我們使用 ConcurrentHashMap 來修改上面的程序,以解決上述程序在執行時產生的異常。
示例 2:
import java.util.*; import java.util.concurrent.*; class Main extends Thread { static ConcurrentHashMap<Integer, String> m = new ConcurrentHashMap<Integer, String>(); public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { } System.out.println("子線程更新映射"); m.put(103, "C"); } public static void main(String arg[]) throws InterruptedException { m.put(101, "A"); m.put(102, "B"); Main t = new Main(); t.start(); Set<Integer> s1 = m.keySet(); Iterator<Integer> itr = s1.iterator(); while (itr.hasNext()) { Integer I1 = itr.next(); System.out.println( "主線程迭代映射和當前條目是:" + I1 + "..." + m.get(I1)); Thread.sleep(3000); } System.out.println(m); } }
輸出
主線程迭代映射和當前條目是:101…A
子線程更新映射
主線程迭代映射和當前條目是:102…B
主線程迭代映射和當前條目是:103…C
{101=A, 102=B, 103=C}
輸出說明:
上述程序中使用的 Class 擴展瞭Thread 類。讓我們看看控制流,所以我們知道在 ConcurrentHashMap 中,當一個線程正在迭代時,剩餘的線程可以以安全的方式執行任何修改。上述程序中主線程正在更新Map,同時子線程也在嘗試更新Map對象。本程序不會拋出 ConcurrentModificationException。
Hashtable、Hashmap、ConcurrentHashmap的區別
Hashtable | Hashmap | ConcurrentHashmap |
---|---|---|
我們將通過鎖定整個地圖對象來獲得線程安全。 | 它不是線程安全的。 | 我們將獲得線程安全,而無需使用段級鎖鎖定 Total Map 對象。 |
每個讀寫操作都需要一個objectstotal 映射對象鎖。 | 它不需要鎖。 | 讀操作可以不加鎖執行,寫操作可以用段級鎖執行。 |
一次隻允許一個線程在地圖上操作(同步) | 不允許同時運行多個線程。它會拋出異常 | 一次允許多個線程以安全的方式操作地圖對象 |
當一個線程迭代 Map 對象時,其他線程不允許修改映射,否則我們會得到 ConcurrentModificationException | 當一個線程迭代 Map 對象時,其他線程不允許修改映射,否則我們會得到 ConcurrentModificationException | 當一個線程迭代 Map 對象時,其他線程被允許修改地圖,我們不會得到 ConcurrentModificationException |
鍵和值都不允許為 Null | HashMap 允許一個空鍵和多個空值 | 鍵和值都不允許為 Null。 |
在 1.0 版本中引入 | 在 1.2 版本中引入 | 在 1.5 版本中引入 |
到此這篇關於Java中ConcurrentHashMap是如何實現線程安全的文章就介紹到這瞭,更多相關Java ConcurrentHashMap線程安全內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java集合快速失敗與安全失敗解析
- Java十分鐘精通集合的使用與原理下篇
- java迭代器原理及迭代map的四種方式
- Java日常練習題,每天進步一點點(63)
- 深入解讀Java三大集合之map list set的用法