同步容器類
- Vector嗦随、Hashtable,及由Collections.synchronizedXxx等工程方法創(chuàng)建的類。
- 這些類實現(xiàn)線程安全方式是:將它們的狀態(tài)封裝起來,并對每個公有方法都進行同步婉支,使得每次只有一個線程能方法容器狀態(tài)。
同步容器類的問題
- 客戶端新建的復(fù)合操作需要通過容器類的鎖來保證原子性澜建。
- 在迭代操作中向挖,多線程情況下回出現(xiàn)ArrayIndexOutOfBoundsException異常,如果要解決炕舵,則需要在客戶端加鎖何之,但是犧牲了一些伸縮性。
迭代器與CncurrrentModificationException
- 發(fā)現(xiàn)容器在迭代過程中被修改過咽筋,會拋出CME異常溶推。實現(xiàn)原理:將容器計數(shù)器與容器關(guān)聯(lián)起來:如果在迭代期間計數(shù)器被修改,那么hasNext或next將拋出CME異常。然后悼潭,這種檢查在沒有同步的情況下進行的庇忌,因此可能看到失效的計數(shù)值:ABA問題舞箍。
隱藏的迭代器
- 容器的toString舰褪、hashCode、equals疏橄、containsAll占拍、removeAll、retainAll以及包容器作為參數(shù)的構(gòu)造函數(shù)都會對容器進行迭代捎迫,在并發(fā)下就可能出現(xiàn)CME異常晃酒。所以在并發(fā)情況下,在迭代期間需要對容器加鎖窄绒。
并發(fā)容器
- 并發(fā)容器為了改變同步容器的性能贝次,同步容器對容器狀態(tài)的訪問時串行化,以實現(xiàn)線程安全彰导,這種方法降低了并發(fā)性蛔翅,和吞吐量。
- 并發(fā)容器針對多線程并發(fā)訪問設(shè)計的位谋,如果需要提高并發(fā)性使用以下替代方案山析。
- ConcurrentHashMap代替同步且散列的Map.
- CopyOnWriteArrayList用于在遍歷操作為主要操作的情況下代替同步的List。
- ConcurrentMap接口中增加了常見復(fù)合操作的支持:“若沒有則添加”掏父、替換笋轨、有條件刪除。
- ConcurrentSkipListMap代替SortedMap
- ConcurrentSkipListSet代替SortedSet
*并發(fā)容器類提供的迭代器不會拋出CME異常赊淑。
Queue爵政、BlockingQueue容器接口
- Queue用來臨時保存一組待處理元素,實現(xiàn)類包括:ConcurrentLinkedQueue(先進先出隊列)陶缺、PriorityQueue(優(yōu)先隊列茂卦,非并發(fā))
- Queue上的操作不會阻塞,如果隊列為空,返回空值组哩。
- Queue通過LinkedList來實現(xiàn)的等龙。
- BlockingQueue擴展Queue,增加了可阻塞的插入和獲取等操作,如果隊列為空伶贰,那么獲取元素操作將一直阻塞蛛砰,直到隊列中出現(xiàn)一個可用元素。插入時黍衙,如果隊列已滿(對于有界隊列來說泥畅,無界隊列除外),插入操作將一直阻塞琅翻,知道隊列中出現(xiàn)可用空間位仁。適合“生產(chǎn)者-消費者模式”
- BlockingQueue提供了offer方法柑贞,當隊列滿時offer添加元素失敗返回false,此時可以做相應(yīng)策略調(diào)整(減少生產(chǎn)線程或序列化數(shù)據(jù)到磁盤)聂抢,同樣也提供了poll方法钧嘶,在一定時間內(nèi)如果沒有獲取到元素則返回null。
ConcurrentHashMap
- 使用分段鎖(Lock Striping)來提高并發(fā)性和伸縮性琳疏。
- size和isEmpty返回的結(jié)果可能會過期有决,實際上他只是一個估值,但這兩個方法用的不多空盼。
- 在CHM中沒有實現(xiàn)對Map加鎖以提供獨占方法书幕,在Hashtable、synchronizedMap中獲得Map的鎖能防止其他線程訪問這個Map
- 與Hashtable揽趾、synchronizedMap相比台汇,CHM有更多優(yōu)勢,大多數(shù)情況下使用CHM來提高代碼的伸縮性篱瞎,只有當程序需要加鎖Map以進行獨占訪問苟呐,或者需要依賴同步Map帶來的一些其他作用是才應(yīng)該放棄CHM.
CopyOnWriteArrayList、CopyOnWriteArraySet
- 寫入時復(fù)制容器奔缠,在修改的時候會創(chuàng)建并發(fā)布一個新的容器副本掠抬。
- 修改方法(刪除、添加)是加鎖的校哎,避免多個線程調(diào)用產(chǎn)生多個副本两波。
- 修改時會復(fù)制底層數(shù)組,所以需要一定開銷闷哆,所以適合讀遠大于寫的業(yè)務(wù)場景腰奋。
Java并發(fā)編程:并發(fā)容器之CopyOnWriteArrayList(轉(zhuǎn)載)
阻塞隊列和生產(chǎn)者-消費者模式
- BlockingQueue的多種實現(xiàn):LinkedBlockingQueue、ArrayBlockingQueue是FIFO隊列抱怔。PriorityBlockingQueue是按優(yōu)先級排序隊列劣坊,即可根據(jù)元組自然排序來比較元素(如果他們實現(xiàn)Comparable方法),也可以使用Comparator來比較屈留。
雙端隊列
- Deque(發(fā)音“deck”)和BlockingDeque對Queue局冰、BlockingQueue進行了擴展。Deque是一個雙端隊列灌危,實現(xiàn)了在隊列頭和尾高效插入和移除康二。
- 實現(xiàn)為ArrayDeque、LinkedBlockingQueue
阻塞方法與中斷方法
阻塞:等待I/O操作結(jié)束勇蝙、等待獲取一個鎖沫勿,等待從Thread.sleep方法中醒來、等待另一個線程的計算結(jié)果。阻塞線程處于阻塞狀態(tài)产雹,等待某個不受它控制的事件發(fā)生后才能繼續(xù)執(zhí)行诫惭,當外部事件發(fā)生時,線程被置回Runnable(就緒狀態(tài))蔓挖。
LinkedBlockingQueue的take夕土、put等方法會拋出InterruptedException與Thread.sleep一樣。當方法拋出InterruptedException異常时甚,表示該方法時一個阻塞方法隘弊,如果這個方法被中斷哈踱,那么它將努力提前結(jié)束阻塞狀態(tài)荒适。(只是努力不是保證)
LinkedBlockingQueue的take阻塞方法源碼
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
- 中斷,Thread類提供了interrupt方法开镣,用于中斷線程或者查詢線程是否已經(jīng)被中斷刀诬。每個線程都有一個布爾類型的屬性,標識線程的中斷狀態(tài)邪财,當中斷線程時將設(shè)置這個狀態(tài)陕壹。
- 中斷是一種協(xié)作機制,一個線程不能強制其它線程停止正在執(zhí)行的操作而去執(zhí)行其它的操作树埠。
- A線程中斷B時糠馆,A僅僅是要求B在執(zhí)行到某個可以暫停的地方停止正在執(zhí)行的動作---前提是如果線程B愿意停止下來。