Java併發編程藝術 6 Java併發容器和框架

第六章 Java併發容器和框架

ConcurrentHashMap
ConcurrentHashMap是線程安全的HashMap,保證線程安全的同時保證高效。是HashMap和HashTable的進化版。

HashMap和HashTable的區別
HashMap和HashTable幾乎是等價的,但還是存在一定的區別。
【1】HashMap不是線程安全的,HashTable是線程安全的。這也是最大的一個區別。
【2】HashMap可以支持null爲鍵值對,HashTable不行。
【3】HashMap的迭代器是fail-fast迭代器,HashTable的迭代器不是fail-fast。 當有其它線程改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。
【4】因爲HashTable是線程安全的,在單線程中HashMap的效率更好。
使用線程安全HashMap的方式,建議使用第二種。
1.Map m = Collections.synchronizeMap(hashMap);
2.使用ConcurrentHashMap

線程不安全的HashMap。
在多個線程(幾十幾百個)同時對HashMap進行put操作時,會引起HashMap的死循環。原因是多線程會導致HashMap的Entry鏈表形成環形數據結構。一旦形成環形數據額結構,Entry的next節點永遠不爲空,死循環獲取next節點。
效率低下的HashTable
HashTable使用synchronized來保證線程安全,但在線程競爭激烈的情況下會導致效率極低。當每一個線程使用put時,其他線程的get、put等所有操作都會被阻塞。
ConcurrentHashMap的鎖分段技術提高併發效率。
HashTable效率低是因爲所有線程都競爭同一把鎖。ConcurrentHashMap使用了鎖分段技術,先將數據分成一段一段保存,每一段數據配一個鎖。當一個線程訪問一段數據時,其他的段的數據仍然能被訪問。

ConcurrentHashMap由Segment數組和HashEntry數組組成。Segment是一個可重入的鎖。
Segment保存在HashEntry的鏈表結構,對一個HashEntry操作時需要先獲取對應的Segment鎖。這就實現了分段鎖。


使用線程安全的隊列方式
1.使用阻塞算法的阻塞隊列,通過入隊和出隊加鎖的形式實現阻塞隊列。
2.通過使用循環CAS的方式來實現

ConcurrentLinkedQueue:非阻塞的線程安全隊列
ConcurrentLinkedQueue由head節點和tail節點組成,每個節點指向下一個節點。節點之間使用next進行關聯,組成一張鏈表隊列。
默認下head節點存儲元素爲空,tail等於head。

入隊列:


出隊列:



Java中的阻塞隊列
Java中提供四種處理方式,七種阻塞隊列的具體實現。
四種處理方式:拋出異常、返回特殊值、一直阻塞、超時退出。
【1】拋出異常:add(e)、remove()、element()三個方法分別是插入、移除、檢測操作。如果隊列已滿,使用add會拋出異常illagelStateException。如果隊列爲空,使用remove操作會拋出NoSuchElementException。
【2】返回特殊值:offer(e)、poll()、peek()三個方法。插入成功返回true,移除方法是從隊列取出一個元素,沒有返回null
【3】一直阻塞:put()、take()兩個方法。如果隊列滿,使用put方法,則線程阻塞,直到有其他線程取出了元素,使隊列繼續可用。take也是類似,如果隊列爲空,使用take則阻塞,直到隊列有值。
阻塞式是比較常用的方式,一般是幾個線程進行入隊操作,幾個線程出對操作,這樣就可以實現不同線程之間的數據傳遞。
【4】超時方式offer(e,time,unit)、poll(time,unit)
注意:儘量不要使用無界隊列(沒有設置大小的隊列,默認大小Integer.MAX-VALUE),這回導致put()方法不阻塞,
七種阻塞隊列:前四種比較常用
AraayBlockingQueue:數組結構組成的有界阻塞隊列
LinkedBlockingQueue:鏈表結構的有界阻塞隊列
PriorityBlockingQueue:支持優先級的無界阻塞隊列,小心導致低優先級任務飢餓
DelayQueue:使用優先級隊列實現的無界阻塞隊列
SynchronousQueue:不存儲元素的阻塞隊列
LinkedTransferQueue:鏈表結構的無界阻塞隊列
LinkedBlockingQueue:鏈表結構的雙向阻塞隊列

【1】AraayBlockingQueue:
數組結構組成的有界阻塞隊列,使用了先進先出(FIFO)的原則。默認下不支持線程公平訪問隊列(非公平性:阻塞線程爭奪線程訪問資格,可能後阻塞的線程先訪問,極端情況下導致某個線程永遠阻塞。非公平性導致飢餓)。
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true)。設爲true。線程公平。
【2】LinkedBlockingQueue
鏈表結構的有界阻塞隊列。默認和最大長度Integer.MAX-VALUE。也是按照先入先出原則
【3】PriorityBlockingQueue
支持優先級的無界阻塞隊列。默認下使用自然順序升序。通過自定義類實現compareTo()方或者初始化時添加Comparator對元素進行排序。注意不能保證同優先級元素的順序。
【4】DelayQueue
支持延時獲取元素的無界阻塞隊列。使用PriorityQueue來實現。隊列中的元素必須實現Delayed接口,在創建元素時可以指定多久才能在隊列中獲取。延遲期滿以後才能獲取
應用:
緩存系統設計:使用DelayQueue保存元素的有效期,使用線程循環檢查DelayQueue,一旦能獲取元素表示緩存有效期到了
定時任務調度:使用DelayQueue保存要定時執行的任務。比如TimerQueue就是用DelayQueue實現的
【5】:SynchronousQueue
【6】:LinkedTransferQueue
【7】:LinkedBlockingQueue
可以從兩端進行插入和刪除操作





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章