一文探究ArrayBlockQueue函數(shù)及應(yīng)用場景
開篇語
隊列在生活中隨處可見,醫(yī)院繳費需要排隊、做核酸需要排隊、汽車等紅綠燈需要排隊等等。

隊列是一個按照先來到就排在前面,后來到排在后面的數(shù)據(jù)結(jié)構(gòu),并且出隊的時候也是按照先來到先出隊。使用數(shù)組和鏈表進行實現(xiàn)。通常用于協(xié)調(diào)任務(wù)的執(zhí)行和數(shù)據(jù)的交換。
介紹
ArrayBlockingQueue 是一個有界阻塞隊列,有界指的是隊列存在一個最大容量;阻塞指的是如果隊列已經(jīng)滿了,想要往隊列繼續(xù)添加元素的話,那么這個操作將會被暫停,直到隊列中有空位才會繼續(xù)完成添加操作。如果隊列已經(jīng)為空,想要從隊列中獲取元素,那么這個操作將會被暫停,直接隊列中存在元素才會繼續(xù)完成獲取操作。
它具有線程安全、性能好、公平鎖選項的特點:
- 線程安全:使用鎖和條件變量實現(xiàn)線程安全,無需額外的同步措施。
- 阻塞操作:當(dāng)隊列滿時,插入操作阻塞;當(dāng)隊列空時,刪除操作阻塞。這有助于避免忙等待和減少無意義的資源消耗。
- 公平鎖選項:支持是否使用公平鎖。避免鎖饑餓。
- 高性能:基于數(shù)組實現(xiàn),內(nèi)存連續(xù)分配,訪問性能較高。
但是同時也存在不靈活、無法支撐高并發(fā)的缺點
- 有界性:隊列的容量固定,不可動態(tài)改變。因此在創(chuàng)建時分配多大容量將成為關(guān)鍵,分配過多會造成資源浪費,分配過少會造成競爭激烈。
- 鎖競爭:在高并發(fā)情況下,鎖競爭可能會導(dǎo)致性能下降。
實現(xiàn)原理
ArrayBlockingQueue 內(nèi)部使用數(shù)組作為元素的存儲結(jié)構(gòu)。
執(zhí)行存取操作時,都必須先獲取鎖,才可以執(zhí)行存取操作,這就保證ArrayBlockingQueue 是線程安全。
ArrayBlockingQueue 通過兩個 Condition 條件隊列,一個 notFull 條件,一個 notEmpty 條件。在對隊列進行插入元素操作時,判斷當(dāng)前隊列已經(jīng)滿,則通過 notFull 條件將線程阻塞,直到其他線程通知該線程隊列可以繼續(xù)插入元素。在對隊列進行移除元素操作時,判斷當(dāng)前隊列已經(jīng)空,則通過 notEmpty 條件阻塞線程,直到其他線程通過該線程可以繼續(xù)獲取元素。
這樣保證線程的存取操作不會出現(xiàn)錯誤。避免隊列在滿時,丟棄插入的元素;也避免在隊列空時取到一個 null 值。
構(gòu)造函數(shù)
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();
}
構(gòu)造函數(shù)中,需要指定隊列的容量和是否使用公平鎖。并且創(chuàng)建了兩個 Condition 條件隊列,分別命名為 notEmpty 和 notFull,這兩個條件隊列是實現(xiàn)阻塞的關(guān)鍵。
通過構(gòu)造函數(shù)我們可以知道為什么它叫有界:因為創(chuàng)建數(shù)組時,需要指定數(shù)組的容量,并且數(shù)組容量不能在運行中動態(tài)擴大。所以隊列的容量是有邊界的,不是無限擴張的。
插入函數(shù)
public void put(E e) throws InterruptedException {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
- 獲取鎖
- 判斷當(dāng)前隊列是否已經(jīng)滿了
- 如果隊列1已經(jīng)滿了,調(diào)用 notFull 條件隊列的 await() 方法,將該線程阻塞,暫停該線程的插入操作。避免內(nèi)部溢出的問題。
- 如果沒有滿,則直接調(diào)用入隊函數(shù) enqueue 插入到隊列末尾。
- 解鎖

獲取函數(shù)
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
- 獲取鎖
- 判斷當(dāng)前隊列是否為空
- 如果隊列沒有元素,調(diào)用 notEmpty 條件隊列的 await() 方法,將該線程阻塞,暫停該線程的獲取操作。避免獲取元素出錯。
- 如果不為空,則直接調(diào)用出隊函數(shù) dequeue 移除隊列第一個元素,并返回給客戶端。
- 釋放鎖

入隊函數(shù)
private void enqueue(E e) {
final Object[] items = this.items;
items[putIndex] = e;
if (++putIndex == items.length) putIndex = 0;
count++;
notEmpty.signal();
}
將元素插入到隊列的尾部,在完成插入操作之后會調(diào)用 notEmpty 對象的 signal 方法,告訴 notEmpty 阻塞隊列,現(xiàn)在隊列中已經(jīng)有元素,之前因為隊列沒有元素而被阻塞的線程,現(xiàn)在可以來獲取元素了。

內(nèi)部維護一個 putIndex,用于表示下一個將要插入元素的坐標(biāo)。當(dāng) putIndex 等于數(shù)組長度時,將會重置為 0。putIndex 是一個從 0 - length 循環(huán)使用的坐標(biāo)。
維護一個 count 變量,用于表示隊列中存在多少元素,在存入的時候增加,在取出的時候減少。

出隊函數(shù)
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E e = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return e;
}
將隊列的第一個元素移除,并返回給客戶端。在完成移除操作之后會調(diào)用 notFull 對象的 signal 方法,告訴 notFull 阻塞隊列,現(xiàn)在隊列中已經(jīng)有空位了,之前因為隊列沒有空位而被阻塞的線程,現(xiàn)在可以繼續(xù)插入元素。

內(nèi)部維護一個 takeIndex,用于表示下一個可以獲取元素的坐標(biāo)。當(dāng) takeIndex 等于數(shù)組長度時,將會重置為 0。takeIndex 是一個從 0 至數(shù)組長度之間循環(huán)使用的坐標(biāo)。

應(yīng)用場景
適用場景
ArrayBlockingQueue 適用于多個線程之間需要共享數(shù)據(jù)、協(xié)調(diào)任務(wù)執(zhí)行的場景。因此可以總結(jié)出以下幾個應(yīng)用場景:
- 線程池:線程池是一個常見的并發(fā)編程模型,它通過線程池中的線程執(zhí)行任務(wù)。并且可以重復(fù)使用這些線程。在線程池中,可以使用 ArrayBlockingQueue 來存儲需要執(zhí)行的任務(wù),以此控制任務(wù)數(shù)量和執(zhí)行順序。當(dāng)線程池中的線程執(zhí)行完任務(wù)之后,可以從 ArrayBlockingQueue 中取出下一個任務(wù)執(zhí)行。
- 生產(chǎn)者-消費者:在生產(chǎn)者-消費者模型中,生產(chǎn)者負(fù)責(zé)生產(chǎn)數(shù)據(jù),消費者負(fù)責(zé)對數(shù)據(jù)進行處理。在這種模式下,ArrayBlockingQueue 可以作為生產(chǎn)者與消費者之間的數(shù)據(jù)通道,保證線程安全和數(shù)據(jù)正確。
實際應(yīng)用場景
- Apache Tomcat Apache Tomcat 是一個流行的 Java Web 應(yīng)用服務(wù)器,它使用 ArrayBlockingQueue 來實現(xiàn)內(nèi)部的請求隊列。當(dāng)請求到達 Tomcat 時,它們被放入一個 ArrayBlockingQueue 中,并由工作線程從隊列中取出并處理請求。
- Netty Netty 是一個高性能的網(wǎng)絡(luò)編程框架,它使用 ArrayBlockingQueue 來實現(xiàn)內(nèi)部的事件隊列。當(dāng)有新的網(wǎng)絡(luò)事件到達時,它們被放入一個 ArrayBlockingQueue 中,并由 IO 線程從隊列中取出并處理事件。
總結(jié)
ArrayBlockingQueue 是一個固定容量,并且采用阻塞方式的隊列。內(nèi)部采用鎖和條件隊列保證了線程安全性。支持公平鎖選項。但是因為采用阻塞機制且容量有限,無法很好滿足高并發(fā)需求。
以上就是一文探究ArrayBlockQueue函數(shù)及應(yīng)用場景的詳細(xì)內(nèi)容,更多關(guān)于ArrayBlockQueue函數(shù)應(yīng)用場景的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring使用RedisTemplate操作Redis數(shù)據(jù)庫
這篇文章主要介紹了spring使用RedisTemplate操作Redis數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
詳解Java的TCP/IP編程學(xué)習(xí)--基于定界符的成幀
這篇文章主要介紹了Java的TCP/IP編程學(xué)習(xí)--基于定界符的成幀,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
鑒權(quán)認(rèn)證+aop+注解+過濾feign請求的實例
這篇文章主要介紹了鑒權(quán)認(rèn)證+aop+注解+過濾feign請求的實例講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
java理論基礎(chǔ)Stream API終端操作示例解析
這篇文章主要為大家介紹了java理論基礎(chǔ)Stream API終端操作示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03

