并發(fā)容器
并發(fā)容器
- ConcurrentHashMap:線程安全的HashMap
- CopyOnWriteArrayList:線程安全的List
- BlockingQueue:這是一個(gè)接口,表示阻塞隊(duì)列,非常適合用于作為數(shù)據(jù)共享的通道
- ConcurrentLinkedQueue:高效的非阻塞并發(fā)隊(duì)列使用鏈表實(shí)現(xiàn)。可以看做一個(gè)線程安全的LinkedList
- ConcurrentSkipListMap:是一個(gè)Map,使用跳表的數(shù)據(jù)結(jié)構(gòu)進(jìn)行快速查找
ArrayList和HashMap
雖然這兩個(gè)類(lèi)不是線程安全的,但是可以用
Collections.synchronizedList(new ArrayList<E>())和
Collections.synchronizedMap(new HashMap<K,V>0)使之變成線程安全的
ConcurrentHashMap和CopyOnWriteArrayList
- 取代同步的HashMap和同步的ArrayList
- 絕大多數(shù)并發(fā)情況下,ConcurrentHashMap和CopyOnWriteArrayList的性能都更好
Map

ConcurrentHashMap

- Java7中的ConcurrentHashMap最外層是多個(gè)segment,每個(gè)segment的底層數(shù)據(jù)結(jié)構(gòu)與HashMap類(lèi)似,仍然是數(shù)組和鏈表組成的拉鏈法
- 每個(gè)segment獨(dú)立上ReentrantLock鎖,每個(gè)segment之間互不影響,提高了并發(fā)效率
- ConcurrentHashMap默認(rèn)有16個(gè)Segments,所以最多可以同時(shí)支持16個(gè)線程并發(fā)寫(xiě)(操作分別分布在不同的Segment上)。這個(gè)默認(rèn)值可以在初始化的時(shí)候設(shè)置為其他值,但是一旦初始化以后,是不可以擴(kuò)容的

- 每個(gè)Node結(jié)點(diǎn)都是獨(dú)立的,并發(fā)度很高。
- 使用鏈表方式解決hash沖突。
- 當(dāng)鏈表的長(zhǎng)度超過(guò)閾值8時(shí),為了提高查詢(xún)速度,將鏈表轉(zhuǎn)換成紅黑樹(shù)。
線程不安全
public class OptionsNotSafe implements Runnable {
private static ConcurrentHashMap<String, Integer> scores = new ConcurrentHashMap<String, Integer>();
public static void main(String[] args) throws InterruptedException {
scores.put("小明", 0);
Thread t1 = new Thread(new OptionsNotSafe());
Thread t2 = new Thread(new OptionsNotSafe());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(scores);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
while (true) {
Integer score = scores.get("小明");
Integer newScore = score + 1;
// scores.put("小明",newScore); 線程不安全
boolean b = scores.replace("小明", score, newScore);
if (b) {
break;
}
}
}
}
}
CopyOnWriteArrayList
- 代替Vector和SynchronizedList,就和ConcurrentHashMap代替SynchronizedMap的原因一樣
- Vector和SynchronizedList的鎖的粒度太大,并發(fā)效率相對(duì)比較低,并且迭代時(shí)無(wú)法編輯
- Copy-On-Write并發(fā)容器還包括CopyOnWriteArraySet,用來(lái)替代同步Set
適用場(chǎng)景
- 讀操作可以盡可能地快,而寫(xiě)即使慢一些也沒(méi)有太大關(guān)系
- 讀多寫(xiě)少:黑名單,每日更新;監(jiān)聽(tīng)器:迭代操作遠(yuǎn)多余修改操作
讀寫(xiě)規(guī)則
- 回顧讀寫(xiě)鎖:讀讀共享、其他都互斥(寫(xiě)寫(xiě)互斥、讀寫(xiě)互斥、寫(xiě)讀互斥)
- 讀寫(xiě)鎖規(guī)則的升級(jí):讀取是完全不用加鎖的,并且更厲害的是寫(xiě)入也不會(huì)阻塞讀取操作。只有寫(xiě)入和寫(xiě)入之間需要進(jìn)行同步等待
缺點(diǎn)
- 數(shù)據(jù)一致性問(wèn)題:CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實(shí)時(shí)一致性。所以如果你希望寫(xiě)入的數(shù)據(jù),馬上能讀到,請(qǐng)不要使用CopyOnWrite容器。
- 內(nèi)存占用問(wèn)題:因?yàn)镃opyOnWrite的寫(xiě)是復(fù)制機(jī)制,所以在進(jìn)行寫(xiě)操作的時(shí)候,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存。
并發(fā)隊(duì)列(Queue)

阻塞隊(duì)列(BlockingQueue)
- 阻塞隊(duì)列是具有阻塞功能的隊(duì)列,所以它首先是一個(gè)隊(duì)列其次是具有阻塞功能。
- 通常,阻塞隊(duì)列的一端是給生產(chǎn)者放數(shù)據(jù)用,另一端給消費(fèi)者拿數(shù)據(jù)用。阻塞隊(duì)列是線程安全的,所以生產(chǎn)者和消費(fèi)者都可以是多線程的

- take()方法:獲取并移除隊(duì)列的頭結(jié)點(diǎn),一旦如果執(zhí)行take的時(shí)候,隊(duì)列里無(wú)數(shù)據(jù),則阻塞,直到隊(duì)列里有數(shù)據(jù)
- put()方法:插入元素。但是如果隊(duì)列已滿,那么就無(wú)法繼續(xù)插入,則阻塞,直到隊(duì)列里有了空閑空間
take和put不會(huì)拋出異常,只會(huì)阻塞住
add,remove,element會(huì)拋出異常
offer,poll,peek
- 是否有界(容量有多大):這是一個(gè)非常重要的屬性無(wú)界隊(duì)列意味著里面可以容納非常多(Integer.MAXVALUE,約為2的31次,是非常大的一個(gè)數(shù),可以近似認(rèn)為是無(wú)限容量)
- 阻塞隊(duì)列和線程池的關(guān)系:阻塞隊(duì)列是線程池的重要組成部分
ArrayBlockingQueue
- 指定容量
- 公平:指定是否需要保證公平,如果想保證公平的話那么等待了最長(zhǎng)時(shí)間的線程會(huì)被優(yōu)先處理,不過(guò)這會(huì)同時(shí)帶來(lái)一定的性能損耗
LinkedBlockingQueue
- 無(wú)界
- 容量Integer.MAX_VALUE
- 內(nèi)部結(jié)構(gòu):Node、兩把鎖。
PriorityBlockingQueue
- 支持優(yōu)先級(jí)
- 自然順序(而不是先進(jìn)先出)
- 無(wú)界隊(duì)列
- PriorityQueue的線程安全版本
SynchronousQueue
- 它的容量為0
- 需要注意的是,SynchronousQueue的容量不是1而是0,因?yàn)镾ynchronousQueue不需要去持有元素,它所做的就是直接傳遞(direct handoff )
- 效率很高
- SynchronousQueue沒(méi)有peek等函數(shù),因?yàn)閜eek的含義是取出頭結(jié)點(diǎn),但是SynchronousQueue的容量是0,所以連頭結(jié)點(diǎn)都沒(méi)有,也就沒(méi)有peek方法。同理,沒(méi)有iterate相關(guān)方法
- 是一個(gè)極好的用來(lái)直接傳遞的并發(fā)數(shù)據(jù)結(jié)構(gòu)
- SynchronousQueue是線程池Executors.newCachedThreadPool()使用的阻塞隊(duì)列

DelayQueue
- 延遲隊(duì)列,根據(jù)延遲時(shí)間排序
- 元素需要實(shí)現(xiàn)Delayed接口,規(guī)定排序規(guī)則
非阻塞并發(fā)隊(duì)列
并發(fā)包中的非阻塞隊(duì)列只有ConcurrentLinkedQueue這一種顧名思義ConcurrentLinkedQueue是使用鏈表作為其數(shù)據(jù)結(jié)構(gòu)的,使用CAS非阻塞算法來(lái)實(shí)現(xiàn)線程安全(不具備阻塞功能),適合用在對(duì)性能要求較高的并發(fā)場(chǎng)景。用的相對(duì)比較少一些

浙公網(wǎng)安備 33010602011771號(hào)