1.并發(fā)容器
1.1.第一類Collection螟凭,也叫做集合
集合的意思就是說(shuō)這個(gè)容器是什么結(jié)構(gòu)板熊,你都可以把一個(gè)元素一個(gè)元素的往里面添加帘饶。從數(shù)據(jù)結(jié)構(gòu)的角度來(lái)說(shuō)甜无,這個(gè)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)也就兩種
連續(xù)存儲(chǔ)的數(shù)組Array,和非連續(xù)存儲(chǔ)的一個(gè)指向另外一個(gè)的鏈表喳张,但是邏輯結(jié)構(gòu)就很多了续镇。Collection又分成了3大類,分別為Set,List,Queue蹲姐,Queue隊(duì)列接口就是為了高并發(fā)準(zhǔn)備的磨取,Set不會(huì)有重復(fù)的元素人柿。隊(duì)列最主要的原因就是實(shí)現(xiàn)了任務(wù)的狀態(tài)和獲取,叫做阻塞隊(duì)列忙厌,其中有一個(gè)子接口Deque叫做雙端隊(duì)列凫岖,一般的隊(duì)列只能一端進(jìn)另外一端出,但是雙端隊(duì)列卻可以同進(jìn)同出逢净。
1.2.第二類Map哥放,集合特殊的變種,一對(duì)一對(duì)的
1.2.1.HashTable
最開始java1.0的容器里只有兩個(gè)爹土,Vector和Hashtable,但是這兩個(gè)容器所有的默認(rèn)方法都是加了鎖synchronized的甥雕,這是最早設(shè)計(jì)不太合理的地方。后來(lái)又加了HashMap,它是完全沒(méi)有加鎖的胀茵,后來(lái)為了適配有鎖的場(chǎng)景又加了SynchronizedMap社露,它是HashMap的加鎖版本。
Vector和Hashtable 自帶鎖琼娘,基本不用
1.2.2.HashMap
我們來(lái)看看這HashMap峭弟,同學(xué)們想一下這個(gè)HashMap往里頭插會(huì)不會(huì)有問(wèn)題,因?yàn)镠ashMap沒(méi)有鎖脱拼,線程不安全瞒瘸。雖然他速度比較快,但是數(shù)據(jù)會(huì)出問(wèn)題熄浓,并且可能報(bào)各種異常情臭。
主要是因?yàn)樗鼉?nèi)部會(huì)把這個(gè)變成TreeNode。具體細(xì)節(jié)不再細(xì)究
package com.learn.thread.six;
import com.learn.thread.six.enums.Constans;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.UUID;
public class TestHashMap {
private static HashMap<UUID, UUID> m = new HashMap<>();
static int count = Constans.COUNT;
static UUID[] keys = new UUID[count];
static UUID[] values = new UUID[count];
static final int THREAD_COUNT = Constans.THREAD_COUNT;
static {
for (int i = 0; i < count; i++) {
keys[i] = UUID.randomUUID();
values[i] = UUID.randomUUID();
}
}
static class Mythread extends Thread {
/**
* 開始位置
*/
int start;
/**
* 每一個(gè)線程放入的個(gè)數(shù)
*/
int gap = count/THREAD_COUNT;
public Mythread(int start) {
this.start = start;
}
@Override
public void run() {
for (int i = start; i < start + gap; i++) {
m.put(keys[i], values[i]);
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Mythread(i * (count/THREAD_COUNT));
}
for (Thread item : threads) {
item.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(m.size());
}
}
}
可以看到赌蔑,HashMap不是線程安全的
1.2.3.SynchronizedHashMap
這個(gè)是給HashMap手動(dòng)加了鎖俯在,它的源碼自己做了一個(gè)Object,然后每次都是SynchronizedObject娃惯,嚴(yán)格來(lái)講他和那個(gè)HashMap效率上區(qū)別不大
package com.learn.thread.six;
import com.learn.thread.six.enums.Constans;
import java.util.*;
public class TestSynchronziedHashMap {
private static Map<UUID, UUID> m = Collections.synchronizedMap(new HashMap<UUID, UUID>());
static int count = Constans.COUNT;
static UUID[] keys = new UUID[count];
static UUID[] values = new UUID[count];
static final int THREAD_COUNT = Constans.THREAD_COUNT;
static {
for (int i = 0; i < count; i++) {
keys[i] = UUID.randomUUID();
values[i] = UUID.randomUUID();
}
}
static class Mythread extends Thread {
/**
* 開始位置
*/
int start;
/**
* 每一個(gè)線程放入的個(gè)數(shù)
*/
int gap = count/THREAD_COUNT;
public Mythread(int start) {
this.start = start;
}
@Override
public void run() {
for (int i = start; i < start + gap; i++) {
m.put(keys[i], values[i]);
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Mythread(i * (count/THREAD_COUNT));
}
for (Thread item : threads) {
item.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(m.size());
// ---------------------------------
start = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10; j++) {
System.out.println(m.get(keys[10]));
}
});
}
for (Thread item : threads) {
item.start();
}
for (Thread item : threads) {
try {
item.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}
}
1.2.4.ConcurrentHashMap
ConcurrentHashMap 是多線程里邊真正用得到的朝巫,這個(gè)效率主要體現(xiàn)在讀上面,由于它往里邊插的時(shí)候做了各種判斷石景,本來(lái)是鏈表的,后來(lái)改成了紅黑樹拙吉,然后又做了各種各樣的CAS判斷潮孽,所以插入的時(shí)候是更低一點(diǎn)。
HashMap和HashTable層雖然讀的效率稍微低一點(diǎn)筷黔,但是他往里插的時(shí)候檢查的東西比較少往史,就加個(gè)鎖往后一插。
所以佛舱,各種Map的使用椎例,看你實(shí)際當(dāng)中的需求挨决。下面用個(gè)程序看看ConcurrentHashMap的查詢速度和插入速度
package com.learn.thread.six;
import com.learn.thread.six.enums.Constans;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class TestConcurrentHashMap {
private static Map<UUID, UUID> m = new ConcurrentHashMap<>();
static int count = Constans.COUNT;
static UUID[] keys = new UUID[count];
static UUID[] values = new UUID[count];
static final int THREAD_COUNT = Constans.THREAD_COUNT;
static {
for (int i = 0; i < count; i++) {
keys[i] = UUID.randomUUID();
values[i] = UUID.randomUUID();
}
}
static class Mythread extends Thread {
/**
* 開始位置
*/
int start;
/**
* 每一個(gè)線程放入的個(gè)數(shù)
*/
int gap = count/THREAD_COUNT;
public Mythread(int start) {
this.start = start;
}
@Override
public void run() {
for (int i = start; i < start + gap; i++) {
m.put(keys[i], values[i]);
}
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Mythread(i * (count/THREAD_COUNT));
}
for (Thread item : threads) {
item.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(m.size());
// ---------------------------------
start = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 10; j++) {
System.out.println(m.get(keys[10]));
}
});
}
for (Thread item : threads) {
item.start();
}
for (Thread item : threads) {
try {
item.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}
}
1.2.5.ConcurrentSkipListMap
通過(guò)跳表來(lái)實(shí)現(xiàn)的高并發(fā)容器,并且這個(gè)Map是有排序的
跳表: 底層本身存儲(chǔ)的是一個(gè)鏈表元素订歪,是排好序的脖祈。如果鏈表是排好序的,往里邊插入和取值都變得特別麻煩刷晋。因?yàn)槟愕脧念^到尾的找盖高。這時(shí)候跳表就出現(xiàn)了
跳表在鏈表的基礎(chǔ)上拿出了一些關(guān)鍵元素,在上面做一層鏈表眼虱,如果這一層數(shù)據(jù)量還是很大喻奥,就在這個(gè)基礎(chǔ)上再抽一些元素做一個(gè)鏈表。所以查找數(shù)據(jù)的時(shí)候是一層一層的往下查找捏悬,實(shí)現(xiàn)上比TreeMap 又容易了很多撞蚕。
下面做一個(gè)小程序,試試這幾個(gè)Map 的性能
package com.learn.thread.six.map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;
public class TestConcurrentSkipListMap {
public static void main(String[] args) {
Map<String, String> map1 = new ConcurrentSkipListMap<>();
Map<String, String> map2 = new ConcurrentHashMap<>();
Map<String, String> map3 = new HashMap<>();
Hashtable<String, String> map4 = new Hashtable<>();
test(map1);
test(map2);
test(map3);
test(map4);
}
public static void test(Map<String, String> map) {
Random random = new Random();
Thread[] tds = new Thread[100];
CountDownLatch count = new CountDownLatch(tds.length);
long start = System.currentTimeMillis();
for (int i = 0; i < tds.length; i++) {
tds[i] = new Thread(() -> {
for (int j = 0; j < 10; j++) {
map.put(UUID.randomUUID().toString(), "a" + random.nextInt(100000));
}
count.countDown();
});
}
Arrays.asList(tds).forEach(item -> item.start());
try {
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
System.out.println(map.size());
}
}
1.2.6.ArraryList
我們先來(lái)認(rèn)識(shí)一下vector到Queue的發(fā)展歷程过牙,下面有一個(gè)程序展示超賣的現(xiàn)象
package com.learn.thread.six.list;
import java.util.ArrayList;
import java.util.List;
public class TestTicketSeller {
private static List<String> tickets = new ArrayList<>();
static {
for (int i = 0; i < 1000; i++) {
tickets.add("票編號(hào)" + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (tickets.size() >0 ){
System.out.println("銷售了 ---" + tickets.remove(0));
}
}).start();
}
}
}
運(yùn)行一下甥厦,我們發(fā)現(xiàn)賣了超過(guò)1000條
1.2.7.Vector
我們來(lái)看看最早的容器Vector,內(nèi)部是帶鎖的,(add 和remove 都是帶鎖的)抒和,但是我們的代碼邏輯中間Vector 是不會(huì)帶鎖的矫渔。
package com.learn.thread.six.list;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class TestSeller2 {
private static Vector<String> tickets = new Vector<>();
static {
for (int i = 0; i < 100; i++) {
tickets.add("票編號(hào)" + i);
}
}
public static void main(String[] args) {
testSeller();
}
public static void testSeller() {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (tickets.size() >0 ){
// 下面這段代碼并沒(méi)有加鎖
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("銷售了 ---" + tickets.remove(0));
}
}).start();
}
}
}
可以看到,中間代碼睡眠2秒是存在線程安全問(wèn)題的摧莽,如果把它去掉庙洼,整個(gè)程序不會(huì)報(bào)異常。所以還是得最外層加一個(gè)鎖镊辕。如下所示
public static void testSynchronizedSeller() {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 保證原子性操作
synchronized (tickets) {
while (tickets.size() >0 ){
// 下面這段代碼并沒(méi)有加鎖
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("銷售了 ---" + tickets.remove(0));
}
}
}).start();
}
}
1.2.8.LinkedList
LinkList是一個(gè)雙鏈表,在添加和刪除元素時(shí)具有比ArrayList更好的性能.但在get與set方面弱于ArrayList.當(dāng)然,這些對(duì)比都是指數(shù)據(jù)量很大或者操作很頻繁油够。
LinkedList并不需要連續(xù)的空間,大小不確定
package com.learn.thread.six.list;
import java.util.Vector;
public class TestLinkedList {
private static Vector<String> tickets = new Vector<>();
static {
for (int i = 0; i < 10; i++) {
tickets.add("票編號(hào)" + i);
}
}
public static void main(String[] args) {
testSeller();
}
public static void testSeller() {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (true) {
synchronized (tickets) {
if (tickets.size() <= 0) {
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("銷售了------" + tickets.remove(0));
}
}
}).start();
}
}
}
1.2.9.Queue
效率最高的就是這個(gè)Queue,這是Java 最新的一個(gè)借口征懈,主要目標(biāo)就是為了高并發(fā)使用的石咬,就是為了多線程,所以以后多線程就考慮Queue卖哎。
Queue 使用最多的就是ConcurrentLinkedQueue,然后里邊沒(méi)有加鎖鬼悠,調(diào)用一個(gè)叫poll的方法去取值,如果取空了就說(shuō)明里面的值已經(jīng)沒(méi)了亏娜。
package com.learn.thread.six.queue;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
public class TestQueue {
static Queue<String> queue = new ConcurrentLinkedDeque<>();
static {
for (int i = 0; i < 10; i++) {
queue.add("票編號(hào)" + i);
}
}
public static void main(String[] args) {
testQueue();
}
public static void testQueue() {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (true) {
String s = queue.poll();
if (s == null) {
return;
}
System.out.println("銷售了" + s);
}
}).start();
}
}
}
1.2.10.CopyOnWrite
再來(lái)看看并發(fā)的時(shí)候經(jīng)常使用的一個(gè)類焕窝,CopyOnWrite ,CopyOnWirteList维贺,CopyOnWirteSet, CopyOnwirte的意思就是復(fù)制,并且是寫時(shí)復(fù)制
public boolean add(E e) {
// 添加的時(shí)候它掂,上鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 原本的數(shù)組
Object[] elements = getArray();
// 原本數(shù)組的長(zhǎng)度
int len = elements.length;
// 調(diào)用native方法進(jìn)行復(fù)制
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 新的元素
newElements[len] = e;
// 替換數(shù)組
setArray(newElements);
// 成功
return true;
} finally {
// 解鎖
lock.unlock();
}
}
package com.learn.thread.six.queue;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
public class TestCopyOnwirte {
public static void main(String[] args) {
List<Integer> list1 = new CopyOnWriteArrayList<>();
List<Integer> list2 = new ArrayList<>();
List<Integer> list3 = new Vector<>();
List<Integer> list4 = new LinkedList<>();
test(list1);
test(list2);
test(list3);
test(list4);
}
public static void test(List<Integer> list) {
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 100; j++) {
int finalJ = j;
new Thread(() -> {
list.add(finalJ);
}) .start();
}
}
System.out.println(list.size());
long end = System.currentTimeMillis();
System.out.println("time" + (end - start));
}
}
可以發(fā)現(xiàn)CopyOnWirte的寫是最慢的,所以這個(gè)適用于溯泣,多讀少寫的場(chǎng)景虐秋。如果對(duì)象很大榕茧,CopyOnWirte存在內(nèi)存溢出的情況,因?yàn)閷懖僮鞫际菑?fù)制一份客给,需要開辟另外一個(gè)空間
3.隊(duì)列
3.1.BlockingQueue
這個(gè)是后面線程池的時(shí)候需要講的內(nèi)存用押,重點(diǎn)就是Block阻塞,顧名思義起愈,阻塞隊(duì)列只恨,我們可以在其方法上做到線程自動(dòng)阻塞。
offer offer是往里頭添加抬虽,加沒(méi)加進(jìn)去會(huì)返回一個(gè)boolean值
add add 同樣往里頭加官觅,但是如果沒(méi)加進(jìn)去是會(huì)拋出異常的
poll 提取數(shù)據(jù),并且remove掉
peek 提取數(shù)據(jù)阐污,但是不remove
package com.learn.thread.six.queue;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
public class TestBlockingQueue {
public static void main(String[] args) {
Queue<String> queue = new ConcurrentLinkedDeque<>();
for (int i = 0; i < 10; i++) {
queue.offer("a" + i);
}
System.out.println(queue);
System.out.println(queue.size());
// 隊(duì)列先進(jìn)后出休涤,poll是提取最后一個(gè)進(jìn)去的
System.out.println(queue.poll());
System.out.println(queue.peek());
System.out.println(queue.size());
}
}
3.2.LinkedBlockingQueue
LinkedBlockingQueue 是用鏈表實(shí)現(xiàn)的無(wú)界隊(duì)列,一直往里頭加笛辟,直到內(nèi)存裝滿了為止
LinkedBlockingQueue 在Queue的基礎(chǔ)上功氨,添加兩個(gè)很重要的方法
put 往隊(duì)列里邊加,加滿了手幢,這個(gè)線程就會(huì)阻塞住捷凄。
take 往外取數(shù)據(jù),如果空了围来,線程也會(huì)阻塞住
所以LinkedBlockingQueue是實(shí)現(xiàn)生產(chǎn)者消費(fèi)者最好的容器跺涤。
下面以一個(gè)小程序演示一下一個(gè)線程a到i 裝數(shù)據(jù),沒(méi)裝一個(gè)睡一秒监透,另外啟5個(gè)線程不斷的從里面take桶错,空了我就等著,什么時(shí)候新加了我就立馬把他取出來(lái)
package com.learn.thread.six.queue;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TestLinkedBlockingQueue {
static BlockingQueue<String> queue = new LinkedBlockingQueue<>(3);
static Random random = new Random();
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 5; i++) {
// 如果滿了就會(huì)等待
try {
queue.put("a" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"p1").start();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
while (true) {
try {
System.out.println(Thread.currentThread().getName() + queue.take());
} catch (Exception ex) {
}
}
}).start();
}
}
}
3.3.ArrayBlockingQueue
ArrayBlockingQueue 是有界的胀蛮,你可以指定一個(gè)固定的值院刁,他的容器就是10,那么當(dāng)你往里面扔內(nèi)容的時(shí)候粪狼,一旦滿了put方法就是阻塞住退腥,如果繼續(xù)add 的話就會(huì)拋出異常.
offer 用返回值來(lái)判斷是否添加成功,還有另外一個(gè)寫法再榄,你可以指定一個(gè)時(shí)間嘗試往里邊添加阅虫,比如一秒鐘,如果一秒鐘內(nèi)沒(méi)有添加成功不跟,他就返回了。
面試題經(jīng)常會(huì)被問(wèn)到 Queue和List 的區(qū)別是什么
答:添加了offer\peek\poll\put\take 這些方法對(duì)線程友好的阻塞或者等待米碰。
package com.learn.thread.six.queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class TestArrayLBlockingQueue {
public static BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
queue.put("a" + i);
}
// 滿了就會(huì)阻塞
queue.put("a");
// queue.add("a");
queue.offer("a");
queue.offer("a", 1, TimeUnit.SECONDS);
System.out.println(queue);
}
}
下面看看幾個(gè)比較特殊的Queue窝革,這些都是BlockingQueue 购城,同樣也是阻塞的
3.4.DelayQueue
DelayQueue 可以實(shí)現(xiàn)時(shí)間上的排序,這個(gè)DelayQueue能實(shí)現(xiàn)按里面等待的時(shí)間來(lái)排序虐译。
但是要實(shí)現(xiàn)Comparable接口重寫compareTo來(lái)確定是怎么排序的瘪板,DelayQueue就是按時(shí)間順序進(jìn)行任務(wù)調(diào)度
package com.learn.thread.six.queue;
import lombok.ToString;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class TestDelayQueue {
private static BlockingQueue queue = new DelayQueue<>();
@ToString
static class MyThread implements Delayed {
String name;
long runningTime;
public MyThread(String name, long runningTime) {
this.name = name;
this.runningTime = runningTime;
}
/**
* 根據(jù)這個(gè)時(shí)間去排序
*
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
// 修改排序
@Override
public int compareTo(Delayed o) {
if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {
return -1;
} else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
return 1;
} else {
return 0;
}
}
public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
MyThread t1 = new MyThread("t1", now + 1000);
MyThread t2 = new MyThread("t2", now + 2000);
MyThread t3 = new MyThread("t3", now + 1500);
MyThread t4 = new MyThread("t4", now + 2500);
MyThread t5 = new MyThread("t5", now + 500);
queue.put(t1);
queue.put(t2);
queue.put(t3);
queue.put(t4);
queue.put(t5);
System.out.println(queue);
for (int i = 0; i < 5; i++) {
System.out.println(queue.take());
}
}
}
}
DelayQueue本質(zhì)上是用了個(gè)PriorityQueue,PriorityQueue是AbstractQueue繼承的。PriorityQueue的特點(diǎn)是它內(nèi)部往里裝的時(shí)候不是按順序的漆诽,而是內(nèi)部進(jìn)行了一個(gè)排序侮攀。按照優(yōu)先級(jí),最小的優(yōu)先厢拭,內(nèi)部結(jié)構(gòu)是一個(gè)二叉樹兰英,這個(gè)二叉樹可以認(rèn)為是堆排序里面那個(gè)最小堆值排在最上面。
package com.learn.thread.six.queue;
import java.util.PriorityQueue;
public class TestPriorityQueue {
public static void main(String[] args) {
PriorityQueue<String> queue = new PriorityQueue<>();
queue.add("a");
queue.add("e");
queue.add("b");
queue.add("c");
queue.add("f");
for (int i = 0; i < 5; i++) {
System.out.println(queue.poll());
}
}
}
3.5.SynchronousQueue
SynchronousQueue的容量為0供鸠,這個(gè)容器不是用來(lái)裝內(nèi)容的畦贸,專門用來(lái)兩個(gè)線程之間傳內(nèi)容的,給線程下達(dá)任務(wù)的楞捂,跟之前講過(guò)的Exchange一樣薄坏。
add方法會(huì)直接報(bào)錯(cuò),原因是容量為0寨闹,不能添加胶坠。只能用put調(diào)用,并且是要求前面有人拿著這個(gè)東西你才可以往里面裝
SynchronousQueue線程池多數(shù)情況下會(huì)使用繁堡,很多線程取任務(wù)沈善,互相之間進(jìn)行任務(wù)調(diào)度的都是他
package com.learn.thread.six.queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
public class TestSynchronousQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 阻塞等待消費(fèi)者消費(fèi),如果沒(méi)有被消費(fèi)會(huì)一直等待
queue.put("aaa");
System.out.println("niadsasdasd");
System.out.println(queue.size());
}
}
3.6.TransferQueue
Transfer意思為傳遞帖蔓,實(shí)際上和SynchronousQueue一樣矮瘟,不過(guò)SynchronousQueue只能傳遞一個(gè),它能傳遞多個(gè)塑娇,并且它還有一個(gè)transfer方法澈侠,意思就是裝完后就在這里等著被消費(fèi)。
一般使用場(chǎng)景埋酬,我做了一件事情哨啃,這個(gè)事情一定要有一個(gè)結(jié)果,有了結(jié)果之后我才可以繼續(xù)進(jìn)行我下面的事情写妥。
比如說(shuō)我支付了錢拳球,這個(gè)訂單付款完了,也是要一直等這個(gè)付賬結(jié)果完了了我才可以給客戶反饋
package com.learn.thread.six.queue;
import java.util.concurrent.LinkedTransferQueue;
import static java.lang.Thread.sleep;
public class TestTransferQueue {
public static void main(String[] args) throws InterruptedException {
LinkedTransferQueue<String> queue = new LinkedTransferQueue<String>();
new Thread(() -> {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
queue.transfer("asdasdasd");
new Thread(() -> {
try {
System.out.println(queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
容器這章主要為了后面的線程池
總結(jié)
從hashTable到ConcurrentHashMap珍特,這不是一個(gè)替代關(guān)系祝峻,而是不同場(chǎng)景下不同的使用
Vector 到Queue這樣一個(gè)過(guò)程,主要是Vector添加了許多對(duì)線程友好的API,offer\peek\poll莱找,它的一個(gè)子類叫BlockingQueue又添加了put和take 實(shí)現(xiàn)了阻塞操作酬姆。