https://blog.csdn.net/wangyj1992/article/details/75449226
比較一下:
HashMap:線程不安全,在多線程使用的時(shí)候需要多加注意。
HashTable:線程安全,使用synchronized關(guān)鍵字修飾,同步方法,通過鎖住整個hash來實(shí)現(xiàn)線程同步,效率低下。
ConcurrentHashMap:線程安全,使用鎖分段技術(shù),控制線程安全。
原理:將數(shù)據(jù)分成一段一段存儲,給每一段加鎖,一個線程在訪問一個數(shù)據(jù)段的時(shí)候,不影響另一個線程對另一個數(shù)據(jù)段的訪問。
ConcurrentHashMap的使用:
ConcurrentHashMap map = new ConcurrentHashMap();
map.put(1, "1");
map.put(2, "2");
和普通的hashmap一樣用。
問題一:怎么知道一個key是放在哪個數(shù)據(jù)段里面呢?
答:ConcurrentHashMap將數(shù)據(jù)分別放到多個Segment中,默認(rèn)16個,每一個Segment中又包含了多個HashEntry列表數(shù)組,
對于一個key,需要經(jīng)過三次hash操作,才能最終定位這個元素的位置,這三次hash分別為:
1、對于一個key,先進(jìn)行一次hash操作,得到hash值h1,也即h1 = hash1(key);
2、將得到的h1的高幾位進(jìn)行第二次hash,得到hash值h2,也即h2 = hash2(h1高幾位),通過h2能夠確定該元素的放在哪個Segment;
3、將得到的h1進(jìn)行第三次hash,得到hash值h3,也即h3 = hash3(h1),通過h3能夠確定該元素放置在哪個HashEntry。
每一個Segment都擁有一個鎖,當(dāng)進(jìn)行寫操作時(shí),只需要鎖定一個Segment,而其它Segment中的數(shù)據(jù)是可以訪問的。
問題二:ConcurrentHashMap是怎么樣實(shí)現(xiàn)線程安全的呢?
ConcurrentHashMap的每個數(shù)據(jù)塊都是一個Segment,每一個Segment都是繼承ReentrantLock (static final class Segment<K,V> extends ReentrantLock implements Serializable),ReentrantLock (java.util.concurrent)是重入鎖,是線程安全的。
問題三:ReentrantLock 和 synchronized的區(qū)別?
參考文章:http://blog.csdn.net/fw0124/article/details/6672522
問題4:get方法的使用
Segment的get操作實(shí)現(xiàn)非常簡單和高效。先經(jīng)過一次再哈希,然后使用這個哈希值通過哈希運(yùn)算定位到segment,再通過哈希算法定位到元素,代碼如下:
1
public V get(Object key) {
2
3
int hash = hash(key.hashCode());
4
5
return segmentFor(hash).get(key, hash);
6
7
}
問題5:put方法的使用
由于put方法里需要對共享變量進(jìn)行寫入操作,所以為了線程安全,在操作共享變量時(shí)必須得加鎖。Put方法首先定位到Segment,然后在Segment里進(jìn)行插入操作。插入操作需要經(jīng)歷兩個步驟,第一步判斷是否需要對Segment里的HashEntry數(shù)組進(jìn)行擴(kuò)容,第二步定位添加元素的位置然后放在HashEntry數(shù)組里。
————————————————
版權(quán)聲明:本文為CSDN博主「wangyj1992」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wangyj1992/article/details/75449226