今天我們來介紹一個(gè)BlockingQueue的實(shí)現(xiàn),ArrayBlockingQueue.
從其名字中,我們就能得知,首先,這是一個(gè)隊(duì)列,其次,這個(gè)隊(duì)列能夠被阻塞,最后,它的底層數(shù)據(jù)結(jié)構(gòu)是一個(gè)數(shù)組.
老規(guī)矩,我們還是先貼上來ArrayBlockingQueue的類注釋:

從類注釋中,我們可以發(fā)現(xiàn)幾點(diǎn)我們剛才沒有提到的:
- 這個(gè)隊(duì)列會(huì)按照FIFO的順序來處理數(shù)據(jù)(這也是隊(duì)列的一個(gè)特性)
- 新元素被插入到隊(duì)列的末尾,而要獲取的元素則位于隊(duì)列的首部.(這里說的隊(duì)列的末尾和隊(duì)列的首部只是一個(gè)邏輯概念,因?yàn)槠涞讓拥臄?shù)據(jù)結(jié)構(gòu)畢竟是一個(gè)數(shù)組)
- 底層數(shù)據(jù)結(jié)構(gòu)數(shù)組的長度不可變
- 向一個(gè)滿隊(duì)列執(zhí)行put()操作會(huì)導(dǎo)致線程阻塞,而向一個(gè)空隊(duì)列執(zhí)行take()操作,也會(huì)導(dǎo)致線程阻塞
- 可以為生產(chǎn)者線程和消費(fèi)者線程指定一個(gè)公平策略,如果指定這樣一個(gè)策略,那么總是會(huì)讓等待時(shí)間最久的線程獲得數(shù)據(jù),或者插入數(shù)據(jù)
- 默認(rèn)情況下,是沒有上述的公平策略的,即當(dāng)有多個(gè)消費(fèi)者在等待時(shí),隨機(jī)選擇一個(gè)線程讓其獲得數(shù)據(jù),對(duì)生產(chǎn)者也這樣
- 公平策略有好處也有壞處,好處是能夠防止饑餓現(xiàn)象,壞處是會(huì)降低吞吐量.
ArrayBlockingQueue的內(nèi)部結(jié)構(gòu)
從上文中,我們也提到了,ArrayBlockingQueue的內(nèi)部的數(shù)據(jù)結(jié)構(gòu)是一個(gè)數(shù)組.
但是,僅僅一個(gè)數(shù)組,是肯定不夠的.
ArrayBlockingQueue中,還定義了這么幾種屬性:
- takeIndex: 表示下次執(zhí)行take(),poll(),peek()或remove()操作時(shí)數(shù)據(jù)的位置
- putIndex: 表示下次執(zhí)行put(), offer(), add()操作時(shí),數(shù)據(jù)的位置
- count: 隊(duì)列中已經(jīng)存在的數(shù)據(jù)的個(gè)數(shù)
那么為什么需要takeIndex和putIndex這么兩個(gè)字段呢?在ArrayList的實(shí)現(xiàn)中,當(dāng)移除元素時(shí),會(huì)涉及到數(shù)組中,數(shù)據(jù)的移動(dòng),所以,進(jìn)行頻繁的數(shù)據(jù)移除操作,是不適合用ArrayList的.同理,由于BlockingQueue的主要用處就是插入數(shù)據(jù)以及刪除(讀取)數(shù)據(jù),如果每次刪除數(shù)據(jù)時(shí),都進(jìn)行數(shù)組中數(shù)據(jù)的移動(dòng),肯定是不適合的.所以,就用這么兩個(gè)指針,分別指向下次讀取數(shù)據(jù)時(shí)的位置,以及插入數(shù)據(jù)時(shí)的位置.
那么,你可能就有疑問了,既然不進(jìn)行數(shù)據(jù)的移動(dòng),而且數(shù)組的容量又是有限的,那么最后takeIndex和putIndex都指向數(shù)組的末尾?。@咋整?
我們可以參考循環(huán)鏈表的形式?。?/p>
當(dāng)takeIndex或者putIndex指向數(shù)組的末尾時(shí),就讓它指向數(shù)組的起始.因?yàn)閿?shù)據(jù)是按照FIFO的順序插入以及處理的,所以,數(shù)組中,takeIndex前面肯定是空的.所以,我們可以按照環(huán)形的方式處理.
這樣確實(shí)方便了數(shù)據(jù)的插入和刪除,那么我們?nèi)绾闻袛嚓?duì)列是否已經(jīng)滿了或者是空的呢?
我們不是有記錄了數(shù)組中數(shù)據(jù)的個(gè)數(shù)的count屬性嘛.我們可以通過這個(gè)屬性來判斷呀.
除了上面介紹的這幾個(gè)屬性,還有三個(gè)跟線程調(diào)度有關(guān)的屬性.
第一個(gè)是一個(gè)ReentrantLock,因?yàn)槲覀冃枰WCArrayBlockingQueue的每個(gè)操作都是線程安全的.
另外兩個(gè)分別是notEmpty和notFull,它們的類型都是Condition,當(dāng)執(zhí)行數(shù)據(jù)讀取的操作比如poll(),如果隊(duì)列為空,那么就通過notEmpty讓當(dāng)前線程阻塞.notFull的用法類似.
ArrayBlockingQueue的相關(guān)操作
BlockingQueue中,定義了好幾個(gè)插入和訪問數(shù)據(jù)的操作,這幾個(gè)操作之間都有什么差別呢?就拿插入數(shù)據(jù)來說吧,有的操作是,如果隊(duì)列已滿,返回一個(gè)boolean值,有的是阻塞當(dāng)前線程,有的是拋出一個(gè)異常.具體的請(qǐng)參照下表:

BlockingQueue的使用場(chǎng)景
由上面的描述,相信你很容易就能看出,用BlockingQueue可以很方便的實(shí)現(xiàn)一個(gè)消息隊(duì)列.