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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java - 线程安全的 HashMap 实现方法及原理

發(fā)布時間:2024/2/28 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java - 线程安全的 HashMap 实现方法及原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自?http://liqianglv2005.iteye.com/blog/2025016

Java HashMap 是非線程安全的。在多線程條件下,容易導(dǎo)致死循環(huán),具體表現(xiàn)為CPU使用率100%。因此多線程環(huán)境下保證 HashMap 的線程安全性,主要有如下幾種方法:

?

  • 使用 java.util.Hashtable 類,此類是線程安全的。
  • 使用 java.util.concurrent.ConcurrentHashMap,此類是線程安全的。
  • 使用 java.util.Collections.synchronizedMap() 方法包裝 HashMap object,得到線程安全的Map,并在此Map上進(jìn)行操作。
  • 自己在程序的關(guān)鍵方法或者代碼段加鎖,保證安全性,當(dāng)然這是嚴(yán)重的不推薦。
  • 為什么 HashMap 非線程安全, 可以參考大神陳皓(weibo賬號:左耳朵耗子)在他自己技術(shù)網(wǎng)站Coolshell上的文章,寫的非常詳細(xì)。文章鏈接(直通車):http://coolshell.cn/articles/9606.html

    ?

    ?

    這里重點分析下上面列舉的幾種方法實現(xiàn)并行安全性的原理:

    ?

    (一)java.util.Hashtable類:類的主要數(shù)據(jù)結(jié)構(gòu)如下:

    ????

    Java代碼??
  • ????/**?
  • ?????*?The?hash?table?data.?
  • ?????*/??
  • ????private?transient?Entry[]?table;??
  • ??
  • private?static?class?Entry<K,V>?implements?Map.Entry<K,V>?{??
  • ????int?hash;??
  • ????K?key;??
  • ????V?value;??
  • ????Entry<K,V>?next;??
  • ? ???

    ?????? 可見,Hashtable 的實現(xiàn)是一個數(shù)組,每個數(shù)組元素是一個LinkList結(jié)構(gòu),因此類的數(shù)據(jù)實際上保存在一個散列表中。這個實現(xiàn)和 HashMap 的實現(xiàn)是一致的。數(shù)據(jù)結(jié)構(gòu)如下:

    ?

    ????? 那么Hashtable如何保證線程安全性的哪?下面是 Hashtable的源碼:

    ?

    Java代碼??
  • public?synchronized?V?get(Object?key)?{??
  • ????Entry?tab[]?=?table;??
  • ????……?//此處省略,具體的實現(xiàn)請參考?jdk實現(xiàn)??
  • ????}??
  • ??
  • public?synchronized?V?put(K?key,?V?value)?{??
  • ????……?//具體實現(xiàn)省略,請參考jdk實現(xiàn)??
  • ????}??
  • ??
  • ??
  • public?synchronized?V?remove(Object?key)?{??
  • ????……?//具體實現(xiàn)省略,請參考jdk實現(xiàn)??
  • ????}??
  • ??
  • ?????
  • ????public?synchronized?void?putAll(Map<??extends?K,???extends?V>?t)?{??
  • ????????for?(Map.Entry<??extends?K,???extends?V>?e?:?t.entrySet())??
  • ????????????put(e.getKey(),?e.getValue());??
  • ????}??
  • ??
  • ??
  • ????public?synchronized?void?clear()?{??
  • ????……?//具體實現(xiàn)省略,請參考jdk實現(xiàn)??
  • ????}??
  • ???? 上面是 Hashtable 提供的幾個主要方法,包括 get(), put(), remove() 等。注意到每個方法本身都是 synchronized 的,不會出現(xiàn)兩個線程同時對數(shù)據(jù)進(jìn)行操作的情況,因此保證了線程安全性,但是也大大的降低了執(zhí)行效率。因此是不推薦的。

    ?

    ?

    (二)使用 java.util.Collections.synchronizedMap(Map<K,V>) 方法進(jìn)行封裝。 方法源代碼如下:

    ?

    Java代碼??
  • public?static?<K,V>?Map<K,V>?synchronizedMap(Map<K,V>?m)?{??
  • ????return?new?SynchronizedMap<K,V>(m);??
  • ????}??
  • ??
  • ??
  • private?static?class?SynchronizedMap<K,V>??
  • ????implements?Map<K,V>,?Serializable?{??
  • ????//?use?serialVersionUID?from?JDK?1.2.2?for?interoperability??
  • ????private?static?final?long?serialVersionUID?=?1978198479659022715L;??
  • ??
  • ????private?final?Map<K,V>?m;?????//?Backing?Map??
  • ????????final?Object??????mutex;????//?Object?on?which?to?synchronize??
  • ??
  • ????SynchronizedMap(Map<K,V>?m)?{??
  • ????????????if?(m==null)??
  • ????????????????throw?new?NullPointerException();??
  • ????????????this.m?=?m;??
  • ????????????mutex?=?this;??
  • ????????}??
  • ??
  • ????SynchronizedMap(Map<K,V>?m,?Object?mutex)?{??
  • ????????????this.m?=?m;??
  • ????????????this.mutex?=?mutex;??
  • ????????}??
  • ??
  • ????public?int?size()?{??
  • ????????synchronized(mutex)?{return?m.size();}??
  • ????????}??
  • ????public?boolean?isEmpty(){??
  • ????????synchronized(mutex)?{return?m.isEmpty();}??
  • ????????}??
  • ????public?boolean?containsKey(Object?key)?{??
  • ????????synchronized(mutex)?{return?m.containsKey(key);}??
  • ????????}??
  • ?????? 從實現(xiàn)源代碼可以發(fā)現(xiàn),其封裝的本質(zhì)和 Hashtable 的實現(xiàn)是完全一致的,即對原Map本身的方法進(jìn)行加鎖,加鎖的對象或者為外部指定共享對象mutex,或者為包裝后的線程安全的Map本身。Hashtable 可以理解為 SynchronizedMap mutex=null 時候的特殊情況。因此這種同步方式的執(zhí)行效率也是很低的

    ???

    ??????? 既然已經(jīng)有了Hashtable, 為什么還需要Collections 提供的這種靜態(tài)方法包裝哪?很簡單,這種包裝是Java Collection Framework提供的統(tǒng)一接口,除了用于 HashMap 外,還可以用于其他的Map。當(dāng)然 除了對Map進(jìn)行封裝,Collections工具類還提供了對 Collection(比如Set,List)的線程安全實現(xiàn)封裝方法,具體請參考 java.util.Colletions 實現(xiàn),其原理和 SynchronizedMap 是一致的。

    ?

    (三) 使用 java.util.concurrent.ConcurrentHashMap 類。并發(fā)編程大師 Doug Lea 出品,絕對精品。這是 HashMap 的線程安全版,同 Hashtable 相比,ConcurrentHashMap 不僅保證了訪問的線程安全性,而且在效率上有較大的提高。

    ?

    ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu)如下(引用圖片地址http://www.yupoo.com/photos/goldendoc/81556254/):



    ?

    可以看出,相對 HashMap 和 Hashtable, ConcurrentHashMap 增加了Segment 層,每個Segment 原理上等同于一個 Hashtable, ConcurrentHashMap 為 Segment 的數(shù)組。下面是 ConcurrentHashMap 的 put 和 get 方法:

    Java代碼??
  • final?Segment<K,V>?segmentFor(int?hash)?{??
  • ????????return?segments[(hash?>>>?segmentShift)?&?segmentMask];??
  • ????}??
  • ??
  • public?V?put(K?key,?V?value)?{??
  • ????????if?(value?==?null)??
  • ????????????throw?new?NullPointerException();??
  • ????????int?hash?=?hash(key.hashCode());??
  • ????????return?segmentFor(hash).put(key,?hash,?value,?false);??
  • ????}??
  • ??
  • public?V?get(Object?key)?{??
  • ????????int?hash?=?hash(key.hashCode());??
  • ????????return?segmentFor(hash).get(key,?hash);??
  • ????}??
  • 向 ConcurrentHashMap 中插入數(shù)據(jù)或者讀取數(shù)據(jù),首先都要講相應(yīng)的 Key 映射到對應(yīng)的 Segment,因此不用鎖定整個類, 只要對單個的 Segment 操作進(jìn)行上鎖操作就可以了。理論上如果有 n 個 Segment,那么最多可以同時支持 n 個線程的并發(fā)訪問,從而大大提高了并發(fā)訪問的效率。另外?rehash() 操作也是對單個的 Segment 進(jìn)行的,所以由 Map 中的數(shù)據(jù)量增加導(dǎo)致的 rehash 的成本也是比較低的。

    ?

    單個 Segment 的進(jìn)行數(shù)據(jù)操作的源碼如下:

    ?

    Java代碼??
  • V?put(K?key,?int?hash,?V?value,?boolean?onlyIfAbsent)?{??
  • ????????????lock();??
  • ????????????try?{??
  • ????????????????int?c?=?count;??
  • ????????????????if?(c++?>?threshold)?//?ensure?capacity??
  • ????????????????????rehash();??
  • ??
  • ?????????????????……?//?代碼省略,具體請查看源碼??????????????????
  • ??
  • ????????????}?finally?{??
  • ????????????????unlock();??
  • ????????????}??
  • ????????}??
  • ??
  • V?replace(K?key,?int?hash,?V?newValue)?{??
  • ????????????lock();??
  • ????????????try?{??
  • ????????????????HashEntry<K,V>?e?=?getFirst(hash);??
  • ????????????????
  • ?????????????????……?//?代碼省略,具體請查看源碼?????
  • ?????????????????
  • ????????????}?finally?{??
  • ????????????????unlock();??
  • ????????????}??
  • ????????}??
  • ?

    ?? 可見對 單個的 Segment 進(jìn)行的數(shù)據(jù)更新操作都是 加鎖的,從而能夠保證線程的安全性。

    ?

    ?

    ConcurrentHashMap 的更具體實現(xiàn)和分析見(直通車)?http://www.iteye.com/topic/1103980, 非常詳細(xì)。

    幾種線程同步實現(xiàn)方法的效率比較,可以參考(直通車)??http://blog.sina.com.cn/s/blog_734a77160100yku1.html


    總結(jié)

    以上是生活随笔為你收集整理的Java - 线程安全的 HashMap 实现方法及原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。