同步容器類
同步容器類Vector 和 Hashtable 他宛,以及一些由 Collections.synchronizedXxx 等工廠方法創(chuàng)建的隔心。其底層的機(jī)制無非是用傳統(tǒng)的synchronized 關(guān)鍵字對每一個公用的方法都進(jìn)行同步喜颁,使得每次只能有一個線程訪問容器的狀態(tài)填硕。這很明顯不滿足我們今天互聯(lián)網(wǎng)時代的高并發(fā)的需求耘戚,在保證線程安全的同事缕陕,也必須要有足夠好的性能峻仇。
同步類容器都是線程安全的篮洁,但在某些場景下需要加鎖來保護(hù)復(fù)合操作。復(fù)合操作包括:迭代(反復(fù)訪問元素删掀,直到遍歷完容器中所有元素)翔冀、跳轉(zhuǎn)(根據(jù)指定順序找到當(dāng)前元素的下一個元素)以及條件運(yùn)算。這些復(fù)合操作在多線程并發(fā)地修改容器時披泪,可能會表現(xiàn)出意外的行為纤子,最經(jīng)典的便是:
ConcurrentModificationException
, 原因是容器迭代的過程中款票,被并發(fā)的修改了內(nèi)容控硼,這是由于早期迭代設(shè)計的時候并沒有考慮并發(fā)修改的問題。-
同步容器將所有對容器的狀態(tài)的訪問都串行話艾少,以實(shí)現(xiàn)他們的線程安全性卡乾。這種方法的代價是嚴(yán)重降低并發(fā)性,當(dāng)多個線程競爭容器鎖時缚够,吞吐量將嚴(yán)重降低幔妨。
//-----------復(fù)合操作時鹦赎,并不能保證線程安全性-------------------- final Vector<String> tickets = new Vector<>(); for(int i = 1; i<= 1000; i++){ tickets.add("火車票"+i); } // 在迭代的過程中,被后面的線程并發(fā)的修改了迭代器中的內(nèi)容误堡,就會拋出 ConcurrentModificationException for (Iterator iterator = tickets.iterator(); iterator.hasNext(); ) { String string = (String) iterator.next(); tickets.remove(20); } for(int i = 1; i <=10; i ++){ new Thread("線程"+i){ public void run(){ while(true){ if(tickets.isEmpty()) break; System.out.println(Thread.currentThread().getName() + "---" + tickets.remove(0)); } } }.start(); }
并發(fā)容器類
- JDK1.5 提供了多種并發(fā)容器類來改進(jìn)同步容器的性能钙姊。ConcurrentHashMap, 用來代替同步且基于散列的Map(HashTable),而且在ConcurrentHashMap 中埂伦,添加了一些常見復(fù)合操作的支持(如:“若沒有則添加煞额,替換以及有條件刪除等”)。以及 CopyOnWriteArrayList 沾谜,用于遍歷操作為主要操作的情況下代替同步的List(Voctor)膊毁。
- JDK1.5 還增加了兩種新的容器類型: Queue 和 BlockingQueue, Queue 用來臨時保存一組等待處理的元素基跑。
- 高性能隊(duì)列 ConcurrentLinkedQueue婚温,這是一個傳統(tǒng)的先進(jìn)先出的隊(duì)列。
- 帶優(yōu)先級的 PriorityQueue, 這是一個非并發(fā)的優(yōu)先隊(duì)列
- BlockingQueue 擴(kuò)張了 Queue媳否, 增加拉可阻塞的插入和獲取元素等操作栅螟。如果隊(duì)列為空,那么獲取元素的操作將一直阻塞篱竭,知道隊(duì)列中出現(xiàn)一個可用的元素力图。 如果隊(duì)列已滿(對于有界隊(duì)列來說),那么插入元素的操作將一直阻塞掺逼,知道隊(duì)列中出現(xiàn)可用的空間吃媒。適用于生產(chǎn)者-消費(fèi)者設(shè)計模式。
- 其他實(shí)現(xiàn)Queue的類:
- LinkedBlockingQueue
- ArrayBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- Java6 引入了 ConcurrentSkipListMap吕喘、ConcurrentSkipListSet赘那, 分別作為同步的 SortedMap 和 SortedSet 的并發(fā)替代品。例如用(synchronizedMap 包裝的TreeMap 或 TreeSet)
ConcurrentMap
- ConcurrentMap 接口下有兩個重要的實(shí)現(xiàn):
- ConcurrentHashMap
- ConcurrentSkipListMap (支持并發(fā)排序功能氯质,彌補(bǔ) ConcurrentHashMap)
- ConcurrentHashMap 內(nèi)部使用段(Segment)來表示這些不同的部分募舟,每個段其實(shí)就是一個小的HashTable,它們有自己的鎖闻察。只要多個修改操作發(fā)生在不同的段上拱礁,它們就可以并發(fā)進(jìn)行。把一個整體分成了16個段(Segment)蜓陌。也就是最高支持16個線程的并發(fā)修改操作觅彰。這也是在多線程場景時減小鎖的粒度從而降低鎖競爭的一種方案吩蔑,并且代碼中大多共享變量使用volatile關(guān)鍵字聲明钮热,目的是第一時間獲取修改的內(nèi)容。性能非常好烛芬。
Copy-On-Write容器
- Copy-On-Write 簡稱 COW隧期,是一種用于程序設(shè)計中的優(yōu)化策略飒责。
- JDK里的COW容器有兩種: CopyOnWriteArrayList 和 CopyOnWriteArraySet, COW容器非常有用仆潮,可以在非常多的并發(fā)場景中使用到
-
什么是 CopyOnWrite 容器:
- CopyOnWrite 容器即 寫時復(fù)制 的容器宏蛉。通俗的理解是當(dāng)我們往一個容器添加元素的時候,不直接往當(dāng)前容器添加性置,而是先將當(dāng)前容器進(jìn)行Copy拾并,復(fù)制出一個新的容器。這樣做的好處是我們可以對CopyOnWrite 容器進(jìn)行并發(fā)的讀鹏浅,而不需要加鎖嗅义,因?yàn)楫?dāng)前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想隐砸,讀和寫在不同的容器中進(jìn)行之碗。
- 當(dāng)新容器完成寫操作之后,會把原來容器的引用指向新的容器上季希。