? ? 前面一文介紹了有界鏈表阻塞隊(duì)列LinkedBlockingQueue,今天來(lái)說(shuō)一說(shuō)有界數(shù)組阻塞隊(duì)列ArrayBlockingQueue导坟。
? ??ArrayBlockingQueue類(lèi)中的常用方法和前文介紹的LinkedBlockingQueue類(lèi)常用方法一樣屿良,內(nèi)部源碼實(shí)現(xiàn)也大同小異。ArrayBlockingQueue類(lèi)初始化一個(gè)固定大小的數(shù)組items惫周,使用ReentrantLock保證數(shù)據(jù)的原子性尘惧,相比于LinkedBlockingQueue有兩個(gè)ReentrantLock對(duì)象,ArrayBlockingQueue類(lèi)中只有一個(gè)ReentrantLock對(duì)象递递,讀寫(xiě)操作都需要獲取同一個(gè)ReentrantLock對(duì)象喷橙。另外,還有兩個(gè)條件變量登舞,條件變量?jī)?nèi)部都有一個(gè)條件隊(duì)列用來(lái)存放進(jìn)隊(duì)和出隊(duì)阻塞的線程贰逾,其實(shí)就是生產(chǎn)者-消費(fèi)者模型。這些主要的成員變量如下:
final Object[]items;
final ReentrantLocklock;
private final ConditionnotEmpty;
private final ConditionnotFull;
offer(E e)
? ? 向隊(duì)里尾部插入一個(gè)元素菠秒,如果隊(duì)列有空閑則插入成功后返回true疙剑,如果隊(duì)列已滿則丟棄當(dāng)前元素并返回false。
public boolean offer(E e) {
//(1)檢查e是否為null,是則拋出NullPointerException
? ? checkNotNull(e);
//(2)嘗試獲取lock對(duì)象言缤。
? ? final ReentrantLock lock = this.lock;
? ? lock.lock();
? ? try {
//(3)如果隊(duì)列已滿則丟棄當(dāng)前元素并返回false
? ? ? ? if (count == items.length)
? ? ? ? ? ? return false;
? ? ? ? else {
//(4)隊(duì)列未滿嚼蚀,則在隊(duì)尾添加元素,并調(diào)用notEmpty.signal()管挟,通知阻塞的消費(fèi)者可以進(jìn)行消費(fèi)數(shù)據(jù)轿曙。
? ? ? ? ? ? enqueue(e);
? ? ? ? ? ? return true;
? ? ? ? }
? ? } finally {
//(5)釋放鎖。
? ? ? ? lock.unlock();
? ? }
}
put(E e)
????向隊(duì)列隊(duì)尾插入一個(gè)元素僻孝,如果隊(duì)列中有空閑則插入后直接返回导帝,如果隊(duì)列已滿則阻塞當(dāng)前線程,直到隊(duì)列中有空閑插入成功后返回皮璧。線程阻塞時(shí)如果被其他線程設(shè)置了中斷標(biāo)志舟扎,則該線程會(huì)拋出InterruptedException異常分飞。
public void put(E e) throws InterruptedException {
//(1)檢查e是否為null悴务,是則拋出NullPointerException
? ? checkNotNull(e);
//(2)嘗試獲取鎖對(duì)象,調(diào)用的是lockInterruptibly方法譬猫,所以在當(dāng)其他線程設(shè)置了中斷標(biāo)志讯檐,該線程會(huì)拋出InterruptedException異常。
? ? final ReentrantLock lock = this.lock;
? ? lock.lockInterruptibly();
? ? try {
//(3)隊(duì)列已滿則阻塞當(dāng)前線程
? ? ? ? while (count == items.length)
? ? ? ? ? ? notFull.await();
//(4)隊(duì)列未滿染服,則在隊(duì)尾添加元素别洪,并調(diào)用notEmpty.signal(),通知阻塞的消費(fèi)者可以進(jìn)行消費(fèi)數(shù)據(jù)柳刮。
? ? ? ? enqueue(e);
? ? } finally {
//(5)釋放鎖挖垛。
? ? ? ? lock.unlock();
? ? }
}
?poll()?
? ??從隊(duì)列頭部獲取一個(gè)元素,并從隊(duì)列里移除該元素秉颗,如果隊(duì)列為空則返回null痢毒。
public E poll() {
//(1)嘗試獲取鎖。
? ? final ReentrantLock lock = this.lock;
? ? lock.lock();
? ? try {
//(2)隊(duì)列不為空則獲取頭部元素并移除蚕甥,調(diào)用notFull.signal()方法哪替,通知阻塞的生產(chǎn)者可以生產(chǎn)數(shù)據(jù)。
? ? ? ? return (count == 0) ? null : dequeue();
? ? } finally {
//(3)釋放鎖菇怀。
? ? ? ? lock.unlock();
? ? }
}
peek()
? ??獲取隊(duì)列頭部的元素但不從隊(duì)列中移除該元素凭舶,隊(duì)列為空的時(shí)候返回null。
public E peek() {
? ? final ReentrantLock lock = this.lock;
? ? lock.lock();
? ? try {
? ? ? ? return itemAt(takeIndex); // null when queue is empty
? ? } finally {
? ? ? ? lock.unlock();
? ? }
}
take()
? ??獲取隊(duì)列中頭節(jié)點(diǎn)元素并從隊(duì)列里移除該節(jié)點(diǎn)爱沟,如果隊(duì)列為空則會(huì)阻塞當(dāng)前線程直到隊(duì)列中有元素帅霜。如果當(dāng)前線程在阻塞時(shí)被其他線程設(shè)置了中斷標(biāo)志,則會(huì)拋出InterruptedException異常呼伸。
public E take() throws InterruptedException {
//(1)嘗試獲取鎖對(duì)象义屏,調(diào)用的是lockInterruptibly方法,所以在當(dāng)其他線程設(shè)置了中斷標(biāo)志,該線程會(huì)拋出InterruptedException異常闽铐。
? ? final ReentrantLock lock = this.lock;
? ? lock.lockInterruptibly();
? ? try {
//(2)隊(duì)列為空則進(jìn)行阻塞等待蝶怔。
? ? ? ? while (count == 0)
? ? ? ? ? ? notEmpty.await();
//(3)隊(duì)列不為空則獲取頭部元素并移除,調(diào)用notFull.signal()方法兄墅,通知阻塞的生產(chǎn)者可以生產(chǎn)數(shù)據(jù)踢星。
? ? ? ? return dequeue();
? ? } finally {
//(4)釋放鎖。
? ? ? ? lock.unlock();
? ? }
}
size()
? ? 相比于LinkedBlockingQueue類(lèi)中的size方法隙咸,本類(lèi)中的size需要獲取鎖沐悦,因?yàn)楸绢?lèi)中的count變量沒(méi)有volatile變量修飾無(wú)法保證內(nèi)存的可見(jiàn)性。
public int size() {
? ? final ReentrantLock lock = this.lock;
? ? lock.lock();
? ? try {
? ? ? ? return count;
? ? } finally {
? ? ? ? lock.unlock();
? ? }
}
? ??今天的分享就到這五督,有看不明白的地方一定是我寫(xiě)的不夠清楚藏否,所有歡迎提任何問(wèn)題以及改善方法。