五种并发包总结
1、常用的五種并發(fā)包
- ConcurrentHashMap
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- ArrayBlockingQueue
- LinkedBlockingQueue
2、ConcurrentHashMap
- 線程安全的HashMap的實(shí)現(xiàn)
- 數(shù)據(jù)結(jié)構(gòu):一個(gè)指定個(gè)數(shù)的Segment數(shù)組,數(shù)組中的每一個(gè)元素Segment相當(dāng)于一個(gè)HashTable(一個(gè)HashEntry[])
- 擴(kuò)容的話,只需要擴(kuò)自己的Segment而非整個(gè)table擴(kuò)容
- key與value均不可以為null,而hashMap可以
- 向map添加元素
- 根據(jù)key獲取key.hashCode的hash值
- 根據(jù)hash值算出將要插入的Segment
- 根據(jù)hash值與Segment中的HashEntry的容量-1按位與獲取將要插入的HashEntry的index
- 若HashEntry[index]中的HashEntry鏈表有與插入元素相同的key和hash值,根據(jù)onlyIfAbsent決定是否替換舊值
- 若沒有相同的key和hash,直接返回將新節(jié)點(diǎn)插入鏈頭,原來的頭節(jié)點(diǎn)設(shè)為新節(jié)點(diǎn)的next(采用的方式與HashMap一致,都是HashEntry替換的方法)
- ConcurrentHashMap基于concurrencyLevel劃分出多個(gè)Segment來存儲(chǔ)key-value,這樣的話put的時(shí)候只鎖住當(dāng)前的Segment,可以避免put的時(shí)候鎖住整個(gè)map,從而減少了并發(fā)時(shí)的阻塞現(xiàn)象
- 從map中獲取元素
- 根據(jù)key獲取key.hashCode的hash值
- 根據(jù)hash值與找到相應(yīng)的Segment
- 根據(jù)hash值與Segment中的HashEntry的容量-1按位與獲取HashEntry的index
- 遍歷整個(gè)HashEntry[index]鏈表,找出hash和key與給定參數(shù)相等的HashEntry,例如e
- 如沒找到e,返回null
- 如找到e,獲取e.value
- 如果e.value!=null,直接返回
- 如果e.value==null,則先加鎖,等并發(fā)的put操作將value設(shè)置成功后,再返回value值
- 對于get操作而言,基本沒有鎖,只有當(dāng)找到了e且e.value等于null,有可能是當(dāng)下的這個(gè)HashEntry剛剛被創(chuàng)建,value屬性還沒有設(shè)置成功,這時(shí)候我們讀到是該HashEntry的value的默認(rèn)值null,所以這里加鎖,等待put結(jié)束后,返回value值
- 加鎖情況(分段鎖):
- put
- get中找到了hash與key都與指定參數(shù)相同的HashEntry,但是value==null的情況
- remove
- size():三次嘗試后,還未成功,遍歷所有Segment,分別加鎖(即建立全局鎖)
?
3、CopyOnWriteArrayList
- 線程安全且在讀操作時(shí)無鎖的ArrayList
- 采用的模式就是"CopyOnWrite"(即寫操作-->包括增加、刪除,使用復(fù)制完成)
- 底層數(shù)據(jù)結(jié)構(gòu)是一個(gè)Object[],初始容量為0,之后每增加一個(gè)元素,容量+1,數(shù)組復(fù)制一遍
- 遍歷的只是全局?jǐn)?shù)組的一個(gè)副本,即使全局?jǐn)?shù)組發(fā)生了增刪改變化,副本也不會(huì)變化,所以不會(huì)發(fā)生并發(fā)異常。但是,可能在遍歷的過程中讀到一些剛剛被刪除的對象
- 增刪改上鎖、讀不上鎖
- 讀多寫少且臟數(shù)據(jù)影響不大的并發(fā)情況下,選擇CopyOnWriteArrayList
4、CopyOnWriteArraySet
- 基于CopyOnWriteArrayList,不添加重復(fù)元素
5、ArrayBlockingQueue
- 基于數(shù)組、先進(jìn)先出、線程安全,可實(shí)現(xiàn)指定時(shí)間的阻塞讀寫,并且容量可以限制
- 組成:一個(gè)對象數(shù)組+1把鎖ReentrantLock+2個(gè)條件Condition
- 三種入隊(duì)對比
- offer(E e):如果隊(duì)列沒滿,立即返回true; 如果隊(duì)列滿了,立即返回false-->不阻塞
- put(E e):如果隊(duì)列滿了,一直阻塞,直到數(shù)組不滿了或者線程被中斷-->阻塞
- offer(E e, long timeout, TimeUnit unit):在隊(duì)尾插入一個(gè)元素,,如果數(shù)組已滿,則進(jìn)入等待,直到出現(xiàn)以下三種情況:-->阻塞
- 被喚醒
- 等待時(shí)間超時(shí)
- 當(dāng)前線程被中斷
- 三種出對對比
- poll():如果沒有元素,直接返回null;如果有元素,出隊(duì)
- take():如果隊(duì)列空了,一直阻塞,直到數(shù)組不為空或者線程被中斷-->阻塞
- poll(long timeout, TimeUnit unit):如果數(shù)組不空,出隊(duì);如果數(shù)組已空且已經(jīng)超時(shí),返回null;如果數(shù)組已空且時(shí)間未超時(shí),則進(jìn)入等待,直到出現(xiàn)以下三種情況:
- 被喚醒
- 等待時(shí)間超時(shí)
- 當(dāng)前線程被中斷
- 需要注意的是,數(shù)組是一個(gè)必須指定長度的數(shù)組,在整個(gè)過程中,數(shù)組的長度不變,隊(duì)頭隨著出入隊(duì)操作一直循環(huán)后移
- 鎖的形式有公平與非公平兩種
- 在只有入隊(duì)高并發(fā)或出隊(duì)高并發(fā)的情況下,因?yàn)椴僮鲾?shù)組,且不需要擴(kuò)容,性能很高
6、LinkedBlockingQueue
- 基于鏈表實(shí)現(xiàn),讀寫各用一把鎖,在高并發(fā)讀寫操作都多的情況下,性能優(yōu)于ArrayBlockingQueue
- 組成一個(gè)鏈表+兩把鎖+兩個(gè)條件
- 默認(rèn)容量為整數(shù)最大值,可以看做沒有容量限制
- 三種入隊(duì)與三種出隊(duì)與上邊完全一樣,只是由于LinkedBlockingQueue的的容量無限,在入隊(duì)過程中,沒有阻塞等待
總結(jié)
- 上一篇: Vuex 4源码学习笔记 - 通过Vue
- 下一篇: 四川嘉庆恒运:拼多多上买化妆品靠谱不