一、隊(duì)列Queue類(lèi)型
JUC包中隊(duì)列Queue是用于存儲(chǔ)線(xiàn)程任務(wù),常見(jiàn)的Queue類(lèi)型有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue和DelayQueue。
ArrayBlockingQueue
由數(shù)組組成的有界隊(duì)列,隊(duì)列基于數(shù)組實(shí)現(xiàn),容量大小在創(chuàng)建ArrayBlockingQueue對(duì)象時(shí)已定義好,不可擴(kuò)容。
LinkedBlockingQueue
由鏈接節(jié)點(diǎn)組成的可選有界隊(duì)列,隊(duì)列基于數(shù)組實(shí)現(xiàn),容量大小在創(chuàng)建LinkedBlockingQueue對(duì)象時(shí)已定義好,不可擴(kuò)容。
PriorityBlockingQueue
由優(yōu)先級(jí)堆組成的無(wú)界優(yōu)先級(jí)隊(duì)列,內(nèi)部線(xiàn)程是阻塞的,使用必須實(shí)現(xiàn)compareTo方法,這里的無(wú)界是理論上的。
DelayQueue
由優(yōu)先級(jí)堆支持的、基于時(shí)間的調(diào)度隊(duì)列,由優(yōu)先級(jí)堆支持的、基于時(shí)間的調(diào)度隊(duì)列,內(nèi)部基于無(wú)界隊(duì)列PriorityQueue實(shí)現(xiàn),而無(wú)界隊(duì)列基于數(shù)組的擴(kuò)容實(shí)現(xiàn)。入隊(duì)的對(duì)象必須要實(shí)現(xiàn)Delayed接口。
public static void main(String[] args) {
DelayQueue<MovieTiket> delayQueue = new DelayQueue<MovieTiket>();
MovieTiket tiket = new MovieTiket("電影票0",10000);
delayQueue.put(tiket);
MovieTiket tiket1 = new MovieTiket("電影票1",5000);
delayQueue.put(tiket1);
MovieTiket tiket2 = new MovieTiket("電影票2",8000);
delayQueue.put(tiket2);
System.out.println("message:--->入隊(duì)完畢");
while( delayQueue.size() > 0 ){
try {
tiket = delayQueue.take();
System.out.println("電影票出隊(duì):"+tiket.getMsg());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MovieTiket implements Delayed {
//延遲時(shí)間
private final long delay;
//到期時(shí)間
private final long expire;
//數(shù)據(jù)
private final String msg;
//創(chuàng)建時(shí)間
private final long now;
public MovieTiket(String msg , long delay) {
this.delay = delay;
this.msg = msg;
expire = System.currentTimeMillis() + delay; //到期時(shí)間 = 當(dāng)前時(shí)間+延遲時(shí)間
now = System.currentTimeMillis();
}
/**
* 用于延遲隊(duì)列內(nèi)部比較排序 當(dāng)前時(shí)間的延遲時(shí)間 - 比較對(duì)象的延遲時(shí)間
* 越早過(guò)期的時(shí)間在隊(duì)列中越靠前
* @param delayed
* @return
*/
public int compareTo(Delayed delayed) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS)
- delayed.getDelay(TimeUnit.MILLISECONDS));
}
}
二、Condition隊(duì)列
BlockingQueue底層都是基于ReentrantLock 與 Condition隊(duì)列實(shí)現(xiàn)的,這也是Condition隊(duì)列只能在獨(dú)占模式下使用的原因。
多線(xiàn)程下的BlockingQueue是怎樣操作的?

若BlockingQueue初始容量為1,T1 T2 同時(shí)操作BlockingQueue,T1不斷往BlockingQueue放,T2不斷從BlockingQueue 取。T1 在放完第一次后,BlockingQueue已滿(mǎn),無(wú)法繼續(xù)放了,T1阻塞,T2喚醒,直到BlockingQueue容量為0;T2從BlockingQueue取出,取完第一次后,BlockingQueue為空,無(wú)法繼續(xù)取了,T2阻塞,T1喚醒,直到BlockingQueue容量為1。
條件關(guān)鍵:BlockingQueue的容量。

BlockingQueue的創(chuàng)建會(huì)創(chuàng)建一個(gè)ReentrantLock、兩個(gè)隊(duì)列(NotFull與NotEmpty)。
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
ReentrantLock中用于多線(xiàn)程的同步操作,NotFull與NotEmpty用于存儲(chǔ)不同條件的線(xiàn)程任務(wù)。ArrayBlockingQueue的put 和 take操作都需要配合ReentrantLock的使用,只有線(xiàn)程獲取到鎖的線(xiàn)程才能執(zhí)行put 和 take操作。
注意1:BlockingQueue put操作是通過(guò)獲取ReentrantLock鎖,進(jìn)入條件隊(duì)列而不是CLH同步隊(duì)列,同時(shí)線(xiàn)程會(huì)調(diào)用await方法進(jìn)入阻塞狀態(tài)。
注意2:條件隊(duì)列中阻塞線(xiàn)程不會(huì)被喚醒,只有把條件隊(duì)列中的線(xiàn)程移到CLH同步隊(duì)列中才會(huì)被喚醒。

三、HashMap線(xiàn)程不安全
死鎖
Java 1.7 HashMap會(huì)產(chǎn)生死鎖,其數(shù)據(jù)結(jié)構(gòu)為數(shù)組+鏈表。在多線(xiàn)程場(chǎng)景下,擴(kuò)容期間存在節(jié)點(diǎn)位置互換指針引用的問(wèn)題有可能導(dǎo)致閉環(huán),主要原因還是鏈表中節(jié)點(diǎn)在擴(kuò)容的時(shí)候位置發(fā)生了變化。
Java 1.8 HashMap后不會(huì)產(chǎn)生死鎖,其數(shù)據(jù)結(jié)構(gòu)為數(shù)組+紅黑樹(shù)。
數(shù)據(jù)丟失
HashMap的put操作在多線(xiàn)程下有可能產(chǎn)生相同的hashcode,從而造成數(shù)據(jù)覆蓋,進(jìn)而造成數(shù)據(jù)丟失。Java 1.7 1.8 HashMap都會(huì)產(chǎn)生這樣的問(wèn)題。