摘要:是線程安全,性能出色的的線程安全實(shí)現(xiàn),相比較他是線程安全的,相比較他的性能優(yōu)勢(shì)非常明顯。的源碼其實(shí)很簡(jiǎn)單,和的結(jié)構(gòu)一致,但是每一個(gè)方法都是用來(lái)修飾,以保證操作是線程安全的。這樣在多線程的情況下,只有一個(gè)線程獲取鎖操作中的數(shù)據(jù)。
ConcurrentHashMap
ConcurrentHashMap是線程安全,性能出色的Map的線程安全實(shí)現(xiàn),相比較HashMap他是線程安全的,相比較HashTable他的性能優(yōu)勢(shì)非常明顯。他的使用很簡(jiǎn)單,這里主要是想要探究一下ConcurrentHashMap的實(shí)現(xiàn)原理。
在這里一共有 個(gè)問(wèn)題需要搞明白。
ConcurrentHashMap為什么比HashTable的性能要高?
ConcurrentHashMap在JDK8和JDK7有什么變化,為什么會(huì)有這種變化,對(duì)我們開(kāi)發(fā)有什么啟示?
為什么在JDK8中使用Synchronized而不是用ReentrantLock來(lái)實(shí)現(xiàn)加鎖?
帶著這幾個(gè)問(wèn)題我們來(lái)分析一下ConcurrentHashMap的源碼吧。
在JDK8(JDK7也是一樣)中ConcurrentHashMap的定義如下:
public class ConcurrentHashMapextends AbstractMap implements ConcurrentMap , Serializable {
Java7中ConcurrentHashMap的實(shí)現(xiàn)是基于分段鎖實(shí)現(xiàn)的。他的底層數(shù)據(jù)結(jié)構(gòu)仍然是數(shù)組+鏈表,與HashTable不同的是,ConcurrentHashMap的最外層不是一個(gè)大的數(shù)組,而是一個(gè)Segment數(shù)組(分段鎖的實(shí)現(xiàn))。分段鎖減小了鎖的粒度,提高了并發(fā)程度。這也是為什么比HashTable效率要高的原因。
HashTable的源碼其實(shí)很簡(jiǎn)單,HashTable和HashMap的結(jié)構(gòu)一致,但是每一個(gè)方法都是用Synchronized來(lái)修飾,以保證操作是線程安全的。這樣在多線程的情況下,只有一個(gè)線程獲取鎖操作hashTable中的數(shù)據(jù)。而CourrentHashMap則不是,它允許最多有segment數(shù)組長(zhǎng)度個(gè)線程同時(shí)操作ConcurrentHashMap中的數(shù)據(jù)。
ConcurrentHashMap的整體結(jié)構(gòu)如下(圖片來(lái)源:http://www.jasongj.com/java/c...):
ConcurrentHashMap的定義:
public class ConcurrentHashMapextends AbstractMap implements ConcurrentMap , Serializable { private static final long serialVersionUID = 7249069246763182397L; /** * 表的默認(rèn)容量 */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 默認(rèn)擴(kuò)容因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * segments數(shù)組的默認(rèn)長(zhǎng)度,為了能通過(guò)按位與的散列算法來(lái)定位segments數(shù)組的索引,必須保證segments數(shù)組的長(zhǎng)度是2的N次方 */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * HashEntry最大容量 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * segment的最小容量 */ static final int MIN_SEGMENT_TABLE_CAPACITY = 2; /** * segment的最大容量 */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative /** * 重試次數(shù),無(wú)鎖的情況下嘗試兩次 */ static final int RETRIES_BEFORE_LOCK = 2; /** * 散列運(yùn)算的掩碼,等于ssize-1 */ final int segmentMask; /** * 定位參與散列運(yùn)算的位數(shù),等于32-sshift */ final int segmentShift; /** * 定義segment數(shù)組 */ final Segment [] segments;
Segment定義:
static final class Segmentextends ReentrantLock implements Serializable { transient volatile HashEntry [] table; transient int count; transient int modCount; //擴(kuò)容量,默認(rèn)為表的容量*加載因子,實(shí)際量超過(guò)這個(gè)值的時(shí)候,進(jìn)行擴(kuò)容 transient int threshold; //segment中hashEntry的擴(kuò)容因子 final float loadFactor; }
get()操作經(jīng)過(guò)兩次散列找到HashEntry,然后進(jìn)行遍歷的操作,get方法使用的變量都是volatile類型的,可以保證線程可見(jiàn),能夠被多線程同時(shí)讀,并且保證不會(huì)讀取到過(guò)期的值,在get操作中需要讀count和value兩個(gè)共享變量,所以不需要加鎖,volatile字段的寫(xiě)入操作會(huì)先于讀操作,所有即使有一個(gè)線程在修改,get也能獲取到最新的值。
put() 先對(duì)變量的hashCode進(jìn)行一次再散列然后定位到Segment,然后再Segment中進(jìn)行插入操作。如果HashEntry數(shù)組超過(guò)threshold,那么擴(kuò)容,擴(kuò)容只是針對(duì)Segment進(jìn)行擴(kuò)容。
size() 統(tǒng)計(jì)ConcurrentHashMap中元素的個(gè)數(shù),需要對(duì)Segment中所有元素進(jìn)行求和,Segment里全局變量count是一個(gè)volatile類型的變量,累加可以獲取元素的總個(gè)數(shù),但是不一定準(zhǔn)確,因?yàn)槭褂眠^(guò)的count再后面可以改變,最后的方法就是阻塞put,remove,clean等元素操作的方法,但是這樣非常低效。所以Concurrenthashmap通過(guò)嘗試兩次不鎖來(lái)統(tǒng)計(jì)segment的元素大小,如果兩次結(jié)果不一樣,那么使用加鎖的方式來(lái)統(tǒng)計(jì),容器是否變化是通過(guò)modCount是否變化來(lái)確定的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77795.html
摘要:中的使用及在中的沖突方案引言簡(jiǎn)稱是在作為的替代選擇新引入的,是包的重要成員。為了解決在頻繁沖突時(shí)性能降低的問(wèn)題,中使用平衡樹(shù)來(lái)替代鏈表存儲(chǔ)沖突的元素。目前,只有和會(huì)在頻繁沖突的情況下使用平衡樹(shù)。 java中ConcurrentHashMap的使用及在Java 8中的沖突方案 1、引言 ConcurrentHashMap(簡(jiǎn)稱CHM)是在Java 1.5作為Hashtable的替代選擇新...
摘要:在中一般來(lái)說(shuō)通過(guò)來(lái)創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來(lái)研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
摘要:在中一般來(lái)說(shuō)通過(guò)來(lái)創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買(mǎi)奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來(lái)研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢?jiàn)它的使用,在開(kāi)始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽(tīng)名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢?jiàn)它的使用,在開(kāi)始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽(tīng)名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...
閱讀 2848·2021-11-16 11:44
閱讀 1002·2021-10-09 09:58
閱讀 4537·2021-09-24 09:48
閱讀 4493·2021-09-23 11:56
閱讀 2436·2021-09-22 15:48
閱讀 1933·2021-09-07 10:07
閱讀 3228·2021-08-31 09:46
閱讀 535·2019-08-30 15:56