1:ConcurrentHashMap的實現(xiàn)原理和使用
(1)先說一下HashMap?HashMap在并發(fā)執(zhí)行put操作時會引起死循環(huán)勋篓,是以為多線程會導致它的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu)季率。一旦形成環(huán)形數(shù)據(jù)結(jié)構(gòu)膝迎,Entry的next節(jié)點永遠不為空污抬。
HashTable:所有訪問它的線程必須競爭同一把鎖。
(2)ConcurrentHashMap使用鎖分段技術(shù)遣妥。使用的鎖為ReentrentLock.
(3)定位Segment:ConcurrentHashMap會使用Wang/Jenkins has的變種算法對元素的hasCode進行一次再散列品姓。
(4)HashMap的初始化方法是通過initialCapacity,loadFactory和concurrentLevel等幾個參數(shù)來初始化segment數(shù)組、段偏移量segmentShift蜕猫、段掩碼segmentMask和每個segment里的HashEntry數(shù)組來實現(xiàn)的寂曹。
(5)get操作:先經(jīng)過一次再散列(使用key的hashCode),然后使用這個散列值通過散列運算定位到Segment,在通過散列算法定位到元素。高效原因:整個過程不需要加鎖隆圆。它的get方法將要使用的共享變量都定位成了valatile類型漱挚。如:用于統(tǒng)計當前Segement大小的count字段和用于存儲值的HashEntry的value。由于使用了java的內(nèi)存模型happen-before原則渺氧,能夠保證validate字段的寫入先于讀旨涝。所以get操作使用能夠讀取到最新的值。
volatile替換鎖的經(jīng)典場景侣背。
注意:定位segment使用的是元素的hashcode通過再散列后得到的值的高位白华,而定位HashEntry直接使用的是再散列后的值。
(6)put操作:第一步贩耐,判斷是否需要對Segment里的HashEntry數(shù)組進行擴容弧腥;第二步,定位添加元素的位置潮太,然后將其放在HashEntry數(shù)組里管搪。
A: 怎么擴容?
創(chuàng)建一個容量是原來容量2倍的數(shù)組铡买,然后將原數(shù)組里的元素進行再散列后插入到新的數(shù)組里更鲁。為了高效,只是對某個Segment進行擴容寻狂。
(7)size操作
ConcurrentHashMap的做法是先嘗試2次通過不鎖住Segment的方式來統(tǒng)計各個Segment大小岁经。如果統(tǒng)計過程中,容器的count發(fā)生了變化蛇券,則采用加鎖的方式來統(tǒng)計所有Segment的大小缀壤。
A:ConcurrentHashMap是如何判斷在統(tǒng)計的時候容器是否發(fā)生變化呢?
使用modCount變量纠亚,在put塘慕、remove、clean方法里操作元素前都會將變量modeCount進行加1蒂胞,那么在統(tǒng)計size前后比較modCount是否發(fā)生變化即可知道图呢。
2:ConcurrentHashLinkedQueue
(1)類圖
由head節(jié)點和tail節(jié)點組成。每個節(jié)點Node由節(jié)點元素item和指向下一個節(jié)點next的引用組成骗随。默認情況下head節(jié)點存儲的元素為空蛤织,tail節(jié)點等于head節(jié)點。
(2)入隊實現(xiàn)原理:使用CAS算法來入隊鸿染。入隊過程干兩件事:第一是定位出為尾節(jié)點指蚜;第二是使用CAS算法將入隊節(jié)點設(shè)置成尾節(jié)點的next節(jié)點,如不成功則重試涨椒。tail節(jié)點不一定為尾節(jié)點摊鸡。
注意:入隊方法永遠返回true绽媒,所以不要通過返回值判斷入隊是否成功。
(3)出隊實現(xiàn)原理:
出隊列就是從隊列里返回一個節(jié)點元素免猾,并清空該節(jié)點對元素的引用是辕。
3:Java中的阻塞隊列
(1)什么是阻塞隊列?
BlockingQueue是一個支持兩個附加操作的隊列猎提。
支持阻塞的插入方法:當隊列滿時获三,隊列會阻塞插入元素的線程,直到隊列不滿忧侧。
支持阻塞的移除方法:在隊列為空時石窑,獲取元素的線程會等待隊列變?yōu)榉强铡?/p>
阻塞隊列不可用時:插入和移除提供的4種處理方式牌芋。
注意:如果是無界阻塞隊列蚓炬,隊列不可能會出現(xiàn)滿的情況,所以使用put或offer方法永遠不會被阻塞躺屁,而且使用offfer方法永遠返回true.
(2)java里的阻塞隊列
a: ArrayBlockingQueue:一個由數(shù)組結(jié)構(gòu)組成的有界阻塞隊列肯夏。默認情況下是不保證線程公平訪問隊列。構(gòu)造函數(shù)new ArrayBlockingQueue(1000 , true),第二個參數(shù)為true犀暑,可以保證公平訪問隊列驯击。底層實現(xiàn)方式為ReentrantLock.
b: LinkedBlockingQueue:一個由鏈表組成的有界阻塞隊列。
c: PriorityBlockingQueue:一個支持優(yōu)先級排序的無界阻塞隊列耐亏。
d : DelayQueue:一個使用優(yōu)先級隊列實現(xiàn)的無界阻塞隊列徊都。
e : SynchronousQueue:一個不存儲元素的阻塞隊列。非常適合傳遞性場景广辰,負責把生產(chǎn)者線程處理的數(shù)據(jù)直接傳遞給消費者線程暇矫。
f : LinkedTransferQueue:一個由鏈表結(jié)構(gòu)組成的無界阻塞隊列。其中的transfer方法可以把生產(chǎn)者傳入的元素立刻傳輸給消費者择吊。
g : LinkedBlockingDeque:一個由鏈表結(jié)構(gòu)組成的雙向阻塞隊列李根。雙向隊列經(jīng)典運用場景:工作竊取模式。
(3)阻塞隊列的實現(xiàn)原理
如何設(shè)計阻塞隊列几睛?如何生產(chǎn)者和消費者進行高效的通信房轿?(如果隊列為空,消費者一直等待所森,當生產(chǎn)者添加元素時囱持,消費者是如何知道當前隊列里有元素呢?)
實現(xiàn)方式1:使用通知模式實現(xiàn)焕济。即當生產(chǎn)者往滿的隊列里添加元素時會阻塞(例如:LockSupport.park(this)可以實現(xiàn))住生產(chǎn)者纷妆,當消費者消費了一個隊列中的元素后,會通知(例如:Condition可以實現(xiàn))生產(chǎn)者當前隊列可用吼蚁。
4:Fork/Join框架
(1)工作原理
ForkJoinPool由ForkJoinTask數(shù)組和ForkJoinWorkerThread數(shù)組組成凭需,F(xiàn)orkJoinTask數(shù)組負責將存放程序提交給ForkJoinPool的任務问欠,而ForkJoinWorkerThread數(shù)組負責執(zhí)行這些任務。
A:ForkJoinTask的fork方法實現(xiàn)原理
當調(diào)用ForkJoinTasks的fork方法時粒蜈,程序會調(diào)用ForkJoinWorkThread的pushTask方法異步地執(zhí)行這個任務顺献,然后立即返回結(jié)果。
ForkJoinWorkThread的pushTask方法把當前任務存放在ForkJoinTask數(shù)組隊列里枯怖。然后再調(diào)用ForkJoinPool的signalWork方法喚醒或創(chuàng)建一個工作線程來執(zhí)行任務注整。
B:ForkJoinTask的join方法實現(xiàn)原理
join方法的主要作用是阻塞當前線程并等待獲取結(jié)果。
(2)Fork/Join框架是Java7提供的一個用于并行執(zhí)行任務的框架度硝,是一個把大任務分割成若干個小任務肿轨,最終匯總每個小任務結(jié)果后得到大任務結(jié)果的框架。
(3)工作竊取算法(work-stealing)是指某個線程從其他隊列里竊取任務來執(zhí)行蕊程。
工作竊取的運行流程入下圖所示:
通常做法:使用雙端隊列椒袍。被竊取任務的線程永遠從雙端隊列的頭部拿任務執(zhí)行,而竊取任務的線程永遠從雙端隊列的尾部拿任務執(zhí)行藻茂。
優(yōu)點:充分利用線程進行并行計算驹暑,減少線程間的競爭。
缺點:雙端隊列里只有一個任務時辨赐,還是存在競爭优俘。該算法會消耗更多的系統(tǒng)資源,比如創(chuàng)建多個線程和多個雙端隊列掀序。
(4)Fork/Join框架的核心:
a: ?ForkJoinTask:首先創(chuàng)建一個ForkJoin任務帆焕。它提供在任務中執(zhí)行fork和join操作的機制。通常繼承它的2個子類:
RecursiveAction:用于沒有返回結(jié)果的任務不恭。
RecursiveTask:用于有返回結(jié)果的任務叶雹。
b: ?ForkJoinPool:ForkJoinTask需要通過ForkJoinPool來執(zhí)行。
任務分割出的子任務會添加到當前工作線程所維護的雙端隊列中县袱,進入隊列的頭部浑娜。當一個工作線程的隊列里暫時沒有任務時,它會隨機從其他工作線程的隊列的尾部獲取一個任務式散。
(5)Fork/Join框架的異常處理
提供isCompletedAbnormally方法來檢查任務是否已經(jīng)拋出異辰钤猓或者已經(jīng)被取消了。并且可以通過ForkJoinTask的getException方法獲取異常暴拄,它返回Throwable對象漓滔,如果任務取消了則返回CancellationException。如果任務沒有完成或者沒有拋出異常則返回null乖篷。