并发的容器
并發(fā)的容器
文章目錄
- 并發(fā)的容器
- 1:JDK提供的并發(fā)容器
- 2:conlinkedlist
- 3:blockingqueue
- 3.1:你了解什么
- 3.2:arrayblockingqueue
- 3.3:linkedblockingqueue
- 3.4:priorityblockingqueue
- 4:concurrentskiplistmap
- 5:copyonwritearraylist
- 5.1實(shí)現(xiàn)原理
- 5.2 讀取操作
- 5.3寫操作
- 6:concurrenthashmap
1:JDK提供的并發(fā)容器
提供的都在java.util.concurrent包下面
- concurrnethashmap 線程安全的hashmap
- copyonwritearraylist 線程安全的list,在讀多于寫的情況下,非常實(shí)用
- concurrentlinkedlist:高效的并發(fā)隊(duì)列,使用鏈表連接,可以看作是線程安全的linkedlis,這是一個(gè)非阻塞隊(duì)列
- blockingqueue:接口,通過(guò)鏈表,數(shù)組等方式實(shí)現(xiàn)了這個(gè)接口。表示阻塞隊(duì)列,適用于數(shù)據(jù)共享的通道
- concurrnetskiplistmap:跳表的實(shí)現(xiàn),這是一個(gè)map,使用跳表的數(shù)據(jù)結(jié)構(gòu)進(jìn)行快速查詢
2:conlinkedlist
- Java提供的線程安全的queue可以分為阻塞隊(duì)列和非阻塞隊(duì)列,其中阻塞隊(duì)列的典型例子就是blockingqueue,非阻塞隊(duì)列就是concurrentlinkedqueue。阻塞隊(duì)列可以通過(guò)加鎖來(lái)實(shí)現(xiàn),非阻塞隊(duì)列可以使用CAS操作實(shí)現(xiàn)
- 從名字可以看出,concurrentlinkedqueue,這個(gè)使用鏈表來(lái)作為數(shù)據(jù)結(jié)構(gòu),主要使用cas非阻塞的算法來(lái)實(shí)現(xiàn)線程安全
- 適用于對(duì)隊(duì)列讀寫存在多個(gè)線程進(jìn)行的場(chǎng)所,如果對(duì)隊(duì)列加鎖的成本比較高那么就適合使用無(wú)所的concurrenlinkedqueue來(lái)實(shí)現(xiàn)
3:blockingqueue
3.1:你了解什么
- blockingqueue是線程安全的堵塞隊(duì)列,它使用加鎖的機(jī)制來(lái)實(shí)現(xiàn)
- 當(dāng)隊(duì)列的容器已經(jīng)滿了,生產(chǎn)者線程會(huì)被阻塞,直到隊(duì)列沒(méi)有滿
- 當(dāng)隊(duì)列容器為空的時(shí)候,消費(fèi)者線程會(huì)被阻塞,直到隊(duì)列到非空的時(shí)候
3.2:arrayblockingqueue
- 采用數(shù)組的方式來(lái)實(shí)現(xiàn),容量不能改變。
- 不管是插入還是讀取,都需要獲取到鎖才能夠操作
- 默認(rèn)情況下不能夠保證公平性,如果要保證,可以新建立的時(shí)候設(shè)置為true
3.3:linkedblockingqueue
- 基于單向鏈表實(shí)現(xiàn)的阻塞隊(duì)列,具有FIFO的性質(zhì)
- 一般需要在創(chuàng)建的時(shí)候指定大小,如果沒(méi)有指定,容量等于 MAX
3.4:priorityblockingqueue
- 支持優(yōu)先級(jí)的阻塞隊(duì)列,默認(rèn)情況下采用自然排序
- 并發(fā)控制采用reentrantLock,且在創(chuàng)建的時(shí)候必須指定容量的大小
- 不可以插入 null,同時(shí)必須是課比較大小的,否則會(huì) classCastException
4:concurrentskiplistmap
- 給定一個(gè)單鏈表,即使鏈表是有序的,我們想要查詢一個(gè)數(shù)據(jù),也只能從頭遍歷到尾巴上,這樣的效率很低
- 跳表就不一樣,跳表是一種能偶來(lái)快速查找的數(shù)據(jù)結(jié)構(gòu),類似與平衡樹(shù)
- 對(duì)跳表的插入和刪除只需要對(duì)整個(gè)數(shù)據(jù)結(jié)構(gòu)的局部進(jìn)行操作,這樣帶來(lái)的好處就是,你只需要鎖住一部分就可以了
- 而在查詢的性能上,跳表的時(shí)間復(fù)雜度是 log n
- 跳表的本質(zhì)是維持了多個(gè)鏈表,并且每個(gè)鏈表之間是分層的
- 最底層的鏈表維護(hù)了跳表內(nèi)部的所有元素,每上面一層鏈表都是下面一層的子集
- 跳表內(nèi)的所有鏈表都是排序的,查找的時(shí)候,可以從頂部鏈表開(kāi)始,一旦發(fā)現(xiàn)被查找的元素大于當(dāng)前鏈表中的取值,就會(huì)轉(zhuǎn)入下一層鏈表繼續(xù)找。
- 這也就是說(shuō)在查找的過(guò)程中,搜索是跳躍式的,是一種以空間換取時(shí)間的數(shù)據(jù)結(jié)構(gòu)
- 使用跳表實(shí)現(xiàn)map和哈希算法不同之處是:哈希并不會(huì)保存元素的順序,但是跳表之中都是排序的,因此對(duì)跳表進(jìn)行遍歷的時(shí)候,你會(huì)得到一個(gè)有序的結(jié)果。
5:copyonwritearraylist
- 在很多的場(chǎng)景之下,讀操作可能會(huì)遠(yuǎn)遠(yuǎn)的大于寫操作,由于讀操作根本不會(huì)修改原有的數(shù)據(jù),所以對(duì)于每次的讀加鎖是一種資源的浪費(fèi),我們?cè)试S多個(gè)進(jìn)程同時(shí)讀取數(shù)據(jù)
- copyonwritearraylist,讀取是完全不用加鎖的,并且寫入也不會(huì)堵塞讀操作,只有寫入和寫入之間需要進(jìn)行同步的等待
5.1實(shí)現(xiàn)原理
- add,set都是創(chuàng)建底層數(shù)組的新的副本來(lái)實(shí)現(xiàn)的,當(dāng)list需要被修改的時(shí)候,并不修改當(dāng)前的數(shù)據(jù),而是對(duì)原有的數(shù)據(jù)進(jìn)行一次復(fù)制,將修改的內(nèi)容寫入副本。寫完之后,再將修改完的副本替換完原來(lái)的數(shù)據(jù),這樣就可以保證寫數(shù)據(jù)不會(huì)影響讀操作 。
5.2 讀取操作
- 讀取操作沒(méi)有任何的絨布機(jī)制和鎖操作,因?yàn)樽x取不會(huì)被數(shù)據(jù)造成任何的修改
5.3寫操作
- 寫入操作的時(shí)候,加了鎖,避免了多個(gè)線程同時(shí)修改,導(dǎo)致多個(gè)副本出來(lái)
6:concurrenthashmap
- hashmap不是線程安全的,
總結(jié)
- 上一篇: 垃圾回收
- 下一篇: 服务端第四次课程:MVC,控制器,视图渲