日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

非线程安全的HashMap 和 线程安全的ConcurrentHashMap

發(fā)布時間:2025/6/17 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 非线程安全的HashMap 和 线程安全的ConcurrentHashMap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在平時開發(fā)中,我們經常采用HashMap來作為本地緩存的一種實現方式,將一些如系統(tǒng)變量等數據量比較少的參數保存在HashMap中,并將其作為單例類的一個屬性。在系統(tǒng)運行中,使用到這些緩存數據,都可以直接從該單例中獲取該屬性集合。但是,最近發(fā)現,HashMap并不是線程安全的,如果你的單例類沒有做代碼同步或對象鎖的控制,就可能出現異常。

首先看下在多線程的訪問下,非現場安全的HashMap的表現如何,在網上看了一些資料,自己也做了一下測試:

1public?class?MainClass?{
?2????
?3????public?static?final?HashMap<String,?String>?firstHashMap=new?HashMap<String,?String>();
?4????
?5????public?static?void?main(String[]?args)?throws?InterruptedException?{
?6????????
?7????????//線程一
?8????????Thread?t1=new?Thread(){
?9????????????public?void?run()?{
10????????????????for(int?i=0;i<25;i++){
11????????????????????firstHashMap.put(String.valueOf(i),?String.valueOf(i));
12????????????????}
13????????????}
14????????};
15????????
16????????//線程二
17????????Thread?t2=new?Thread(){
18????????????public?void?run()?{
19????????????????for(int?j=25;j<50;j++){
20????????????????????firstHashMap.put(String.valueOf(j),?String.valueOf(j));
21????????????????}
22????????????}
23????????};
24????????
25????????t1.start();
26????????t2.start();
27????????
28????????//主線程休眠1秒鐘,以便t1和t2兩個線程將firstHashMap填裝完畢。
29????????Thread.currentThread().sleep(1000);
30????????
31????????for(int?l=0;l<50;l++){
32????????????//如果key和value不同,說明在兩個線程put的過程中出現異常。
33????????????if(!String.valueOf(l).equals(firstHashMap.get(String.valueOf(l)))){
34????????????????System.err.println(String.valueOf(l)+":"+firstHashMap.get(String.valueOf(l)));
35????????????}
36????????}
37????????
38????}
39
40}
上面的代碼在多次執(zhí)行后,發(fā)現表現很不穩(wěn)定,有時沒有異常文案打出,有時則有個異常出現:


為什么會出現這種情況,主要看下HashMap的實現:
1public?V?put(K?key,?V?value)?{
?2????if?(key?==?null)
?3????????return?putForNullKey(value);
?4????????int?hash?=?hash(key.hashCode());
?5????????int?i?=?indexFor(hash,?table.length);
?6????????for?(Entry<K,V>?e?=?table[i];?e?!=?null;?e?=?e.next)?{
?7????????????Object?k;
?8????????????if?(e.hash?==?hash?&&?((k?=?e.key)?==?key?||?key.equals(k)))?{
?9????????????????V?oldValue?=?e.value;
10????????????????e.value?=?value;
11????????????????e.recordAccess(this);
12????????????????return?oldValue;
13????????????}
14????????}
15
16????????modCount++;
17????????addEntry(hash,?key,?value,?i);
18????????return?null;
19????}
我覺得問題主要出現在方法addEntry,繼續(xù)看:
1void?addEntry(int?hash,?K?key,?V?value,?int?bucketIndex)?{
2????Entry<K,V>?e?=?table[bucketIndex];
3????????table[bucketIndex]?=?new?Entry<K,V>(hash,?key,?value,?e);
4????????if?(size++?>=?threshold)
5????????????resize(2?*?table.length);
6????}
從代碼中,可以看到,如果發(fā)現哈希表的大小超過閥值threshold,就會調用resize方法,擴大容量為原來的兩倍,而擴大容量的做法是新建一個Entry[]:
1void?resize(int?newCapacity)?{
?2????????Entry[]?oldTable?=?table;
?3????????int?oldCapacity?=?oldTable.length;
?4????????if?(oldCapacity?==?MAXIMUM_CAPACITY)?{
?5????????????threshold?=?Integer.MAX_VALUE;
?6????????????return;
?7????????}
?8
?9????????Entry[]?newTable?=?new?Entry[newCapacity];
10????????transfer(newTable);
11????????table?=?newTable;
12????????threshold?=?(int)(newCapacity?*?loadFactor);
13????}
一般我們聲明HashMap時,使用的都是默認的構造方法:HashMap<K,V>,看了代碼你會發(fā)現,它還有其它的構造方法:HashMap(intinitialCapacity, float loadFactor),其中參數initialCapacity為初始容量,loadFactor為加載因子,而之前我們看到的threshold = (int)(capacity* loadFactor);如果在默認情況下,一個HashMap的容量為16,加載因子為0.75,那么閥值就是12,所以在往HashMap中put的值到達12時,它將自動擴容兩倍,如果兩個線程同時遇到HashMap的大小達到12的倍數時,就很有可能會出現在將oldTable轉移到newTable的過程中遇到問題,從而導致最終的HashMap的值存儲異常。

JDK1.0引入了第一個關聯(lián)的集合類HashTable,它是線程安全的。 HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一個不同步的基類和一個同步的包裝器synchronizedMap。synchronizedMap被稱為有條件的線程安全類。
JDK5.0util.concurrent包中引入對Map線程安全的實現ConcurrentHashMap,比起synchronizedMap,它提供了更高的靈活性。同時進行的讀和寫操作都可以并發(fā)地執(zhí)行。

所以在開始的測試中,如果我們采用ConcurrentHashMap,它的表現就很穩(wěn)定,所以以后如果使用Map實現本地緩存,為了提高并發(fā)時的穩(wěn)定性,還是建議使用ConcurrentHashMap。


====================================================================

另外,還有一個我們經常使用的ArrayList也是非線程安全的,網上看到的有一個解釋是這樣:
一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2.增大 Size 的值。
在單線程運行的情況下,如果 Size = 0,添加一個元素后,此元素在位置 0,而且 Size=1;
而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調度線程A暫停,線程 B得到運行的機會。線程B也將元素放在位置0,(因為size還未增長),完了之后,兩個線程都是size++,結果size變成2,而只有items[0]有元素。
util.concurrent包也提供了一個線程安全的ArrayList替代者CopyOnWriteArrayList。

本文轉載自:http://www.blogjava.net/lukangping/articles/331089.html

轉載于:https://www.cnblogs.com/yangkai-cn/p/4016573.html

總結

以上是生活随笔為你收集整理的非线程安全的HashMap 和 线程安全的ConcurrentHashMap的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 五月天婷婷影院 | 制服av在线 | 国产ts丝袜人妖系列视频 | 日韩一区二区三区免费在线观看 | 中国少妇毛片 | 国产一区在线视频 | 高潮毛片无遮挡高清免费 | 国产精品影片 | 超碰97观看 | 4438全国最大成人网 | 人人模人人爽 | 欧美一级片 | 天天草天天干 | 国产一区视频在线播放 | www黄色在线观看 | 国产精品无码久久久久一区二区 | 中文视频一区二区 | 精品人妻无码一区二区性色 | 国产精品无码人妻一区二区在线 | 免费看h网站 | 在线观看视频一区二区三区 | 成人动漫一区二区三区 | 黄色动漫在线免费观看 | 婷婷色激情 | 日韩精品视频一区二区三区 | 国产欧美精品国产国产专区 | 性做久久久久久久免费看 | 妖精视频在线观看 | 欧美视频一区在线 | 中文字幕超碰在线 | 福利一区福利二区 | 日韩一级视频在线观看 | 日本一区二区三区免费电影 | 亚洲精品国产精品乱码不66 | 日韩精品一二三四区 | ass极品水嫩小美女ass | 欧美有码在线 | 国产精品一区二区三区免费看 | 日韩成人在线视频 | 国产白浆视频 | 欧美大片在线看免费观看 | 性欧美videossex精品 | 一区二区内射 | 黄色精品一区 | 成人快手免费看片 | 欧美一级淫片 | 色午夜视频 | 深夜福利1000 | 青青草国产一区二区三区 | 亚洲精品97 | 黄色av网站在线 | 免费在线黄 | 麻豆传媒在线 | 美乳人妻一区二区三区 | 正在播放老肥熟妇露脸 | 狠狠操操| 四虎精品永久在线 | 色超碰 | 国色天香网站 | 黄色在线免费观看视频 | 欧美资源网 | 国产精品丝袜在线 | 一女被多男玩喷潮视频 | h网址在线观看 | 国产av人人夜夜澡人人爽 | 日韩免费久久 | 影音资源av | 制服丝袜亚洲色图 | 91精品婷婷国产综合久久竹菊 | 亚洲中文无码久久 | 在线播放不卡 | 最新激情网| 9191av| 天天看夜夜看 | 国产精品99久久久久久久女警 | 日产精品久久久 | 337p日本欧洲亚洲鲁鲁 | 亚洲精品 日韩无码 | 韩国精品一区二区三区 | 亚洲麻豆精品 | 妓院一钑片免看黄大片 | xxx久久久| 哪里可以看毛片 | 亚洲精品久久久久avwww潮水 | 成人第四色 | 色不卡 | 欧美黄色a级 | 日韩黄色三级 | 日韩一区二区三区不卡 | 一级黄色片在线免费观看 | 黄色的网站在线观看 | 欧美精彩视频 | 欧美三级图片 | 亚洲激情av在线 | 亚洲熟女乱综合一区二区三区 | 国产在线视频不卡 | 婷婷色五 | 一区二区三区欧美在线 | 欧美卡一卡二 |