Java中ConcurrentHashMap是如何實(shí)現(xiàn)線程安全
ConcurrentHashMap是一個(gè)哈希表,支持檢索的全并發(fā)和更新的高預(yù)期并發(fā)。此類遵循與 Hashtable 相同的功能規(guī)范,并包含 Hashtable 的所有方法。ConcurrentHashMap 位于 java.util.Concurrent 包中。
語(yǔ)法:
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable
其中 K 指的是這個(gè)映射所維護(hù)的鍵的類型,V 指的是映射值的類型
ConcurrentHashmap 的需要:
- HashMap雖然有很多優(yōu)點(diǎn),但不能用于多線程,因?yàn)樗皇蔷€程安全的。
- 盡管 Hashtable 被認(rèn)為是線程安全的,但它也有一些缺點(diǎn)。例如,Hashtable 需要鎖定才能讀取打開,即使它不影響對(duì)象。
- n HashMap,如果一個(gè)線程正在迭代一個(gè)對(duì)象,另一個(gè)線程試圖訪問同一個(gè)對(duì)象,它會(huì)拋出 ConcurrentModificationException,而并發(fā) hashmap 不會(huì)拋出 ConcurrentModificationException。
如何使 ConcurrentHashMap 線程安全成為可能?
- java.util.Concurrent.ConcurrentHashMap類通過將map劃分為segment來(lái)實(shí)現(xiàn)線程安全,不是整個(gè)對(duì)象需要鎖,而是一個(gè)segment,即一個(gè)線程需要一個(gè)segment的鎖。
- 在 ConcurrenHashap 中,讀操作不需要任何鎖。
示例 1:
import java.util.*;
import java.util.concurrent.*;
// 擴(kuò)展Thread類的主類
class GFG extends Thread {
// 創(chuàng)建靜態(tài) HashMap 類對(duì)象
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(
"主線程迭代映射和當(dāng)前條目是:"
+ I1 + "..." + m.get(I1));
Thread.sleep(3000);
}
System.out.println(m);
}
}
輸出:
主線程迭代映射和當(dāng)前條目是: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)
輸出說明:
上述程序中使用的類擴(kuò)展了 Thread 類。讓我們看看控制流。所以,最初,上面的java程序包含一個(gè)線程。當(dāng)我們遇到語(yǔ)句 Main t= new Main() 時(shí),我們正在為擴(kuò)展 Thread 類的類創(chuàng)建一個(gè)對(duì)象。因此,每當(dāng)我們調(diào)用 t.start() 方法時(shí),子線程都會(huì)被激活并調(diào)用 run() 方法. 現(xiàn)在主線程開始執(zhí)行,每當(dāng)子線程更新同一個(gè)地圖對(duì)象時(shí),都會(huì)拋出一個(gè)名為 ConcurrentModificationException 的異常。
現(xiàn)在讓我們使用 ConcurrentHashMap 來(lái)修改上面的程序,以解決上述程序在執(zhí)行時(shí)產(chǎn)生的異常。
示例 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(
"主線程迭代映射和當(dāng)前條目是:"
+ I1 + "..." + m.get(I1));
Thread.sleep(3000);
}
System.out.println(m);
}
}
輸出
主線程迭代映射和當(dāng)前條目是:101...A
子線程更新映射
主線程迭代映射和當(dāng)前條目是:102...B
主線程迭代映射和當(dāng)前條目是:103...C
{101=A, 102=B, 103=C}
輸出說明:
上述程序中使用的 Class 擴(kuò)展了Thread 類。讓我們看看控制流,所以我們知道在 ConcurrentHashMap 中,當(dāng)一個(gè)線程正在迭代時(shí),剩余的線程可以以安全的方式執(zhí)行任何修改。上述程序中主線程正在更新Map,同時(shí)子線程也在嘗試更新Map對(duì)象。本程序不會(huì)拋出 ConcurrentModificationException。
Hashtable、Hashmap、ConcurrentHashmap的區(qū)別
| Hashtable | Hashmap | ConcurrentHashmap |
|---|---|---|
| 我們將通過鎖定整個(gè)地圖對(duì)象來(lái)獲得線程安全。 | 它不是線程安全的。 | 我們將獲得線程安全,而無(wú)需使用段級(jí)鎖鎖定 Total Map 對(duì)象。 |
| 每個(gè)讀寫操作都需要一個(gè)objectstotal 映射對(duì)象鎖。 | 它不需要鎖。 | 讀操作可以不加鎖執(zhí)行,寫操作可以用段級(jí)鎖執(zhí)行。 |
| 一次只允許一個(gè)線程在地圖上操作(同步) | 不允許同時(shí)運(yùn)行多個(gè)線程。它會(huì)拋出異常 | 一次允許多個(gè)線程以安全的方式操作地圖對(duì)象 |
| 當(dāng)一個(gè)線程迭代 Map 對(duì)象時(shí),其他線程不允許修改映射,否則我們會(huì)得到 ConcurrentModificationException | 當(dāng)一個(gè)線程迭代 Map 對(duì)象時(shí),其他線程不允許修改映射,否則我們會(huì)得到 ConcurrentModificationException | 當(dāng)一個(gè)線程迭代 Map 對(duì)象時(shí),其他線程被允許修改地圖,我們不會(huì)得到 ConcurrentModificationException |
| 鍵和值都不允許為 Null | HashMap 允許一個(gè)空鍵和多個(gè)空值 | 鍵和值都不允許為 Null。 |
| 在 1.0 版本中引入 | 在 1.2 版本中引入 | 在 1.5 版本中引入 |
到此這篇關(guān)于Java中ConcurrentHashMap是如何實(shí)現(xiàn)線程安全的文章就介紹到這了,更多相關(guān)Java ConcurrentHashMap線程安全內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java通過MySQL驅(qū)動(dòng)攔截器實(shí)現(xiàn)執(zhí)行sql耗時(shí)計(jì)算
本文主要介紹了java通過MySQL驅(qū)動(dòng)攔截器實(shí)現(xiàn)執(zhí)行sql耗時(shí)計(jì)算,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
java實(shí)現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼
java實(shí)現(xiàn)順序結(jié)構(gòu)線性列表的函數(shù)代碼。需要的朋友可以過來(lái)參考下,希望對(duì)大家有所幫助2013-10-10
java 遠(yuǎn)程文件url如何轉(zhuǎn)為輸入流
這篇文章主要介紹了java 遠(yuǎn)程文件url如何轉(zhuǎn)為輸入流方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
java實(shí)現(xiàn)圖片任意角度旋轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)圖片任意角度旋轉(zhuǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
詳解在Spring Boot框架下使用WebSocket實(shí)現(xiàn)消息推送
這篇文章主要介紹了詳解在Spring Boot框架下使用WebSocket實(shí)現(xiàn)消息推送,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-12-12
Java二級(jí)緩存之提升Hibernate應(yīng)用性能的關(guān)鍵詳解
這篇文章主要介紹了Java二級(jí)緩存之提升Hibernate應(yīng)用性能的關(guān)鍵,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
springBoot靜態(tài)資源加載不到,并且配置了也不生效問題及解決
這篇文章總結(jié)了一個(gè)在Spring Boot 2.6.x版本中,由于路徑匹配策略改變導(dǎo)致靜態(tài)資源無(wú)法加載的問題,并提供了解決方案:通過配置類或在配置文件中設(shè)置路徑匹配策略為AntPathMatcher,或者直接降級(jí)Spring Boot版本2025-02-02

