如何分析阻塞隊(duì)列ArrayBlockingQueue源碼,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供成安網(wǎng)站建設(shè)、成安做網(wǎng)站、成安網(wǎng)站設(shè)計(jì)、成安網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、成安企業(yè)網(wǎng)站模板建站服務(wù),10多年成安做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
先直接總結(jié)ArrayBlockingQueue相關(guān)的特性再根據(jù)源碼來進(jìn)行說明,它的主要特性如下:
1、他是一個(gè)由數(shù)組實(shí)現(xiàn)的FIFO有界阻塞隊(duì)列,數(shù)組由final修飾;
2、ArrayBlockingQueue有界且固定,在構(gòu)造函數(shù)時(shí)必須指定大小,確認(rèn)后不支持改變(確定數(shù)組且不可變);
3、在多線程環(huán)境下不保證“公平性”;
4、通過ReentrantLock與Condition實(shí)現(xiàn)線程安全;
主要屬性如下圖:
ArrayBlockingQueue的屬性還是比較簡單,首先是存放數(shù)據(jù)的Object數(shù)組,它是final修飾的,所以隊(duì)列的長度不可變。
然后三個(gè)int屬性分別表示下次獲取數(shù)據(jù)時(shí)應(yīng)該從數(shù)組的哪里獲取,下次保存數(shù)據(jù)時(shí)應(yīng)該保存到數(shù)組的哪里,count記錄著還有多少個(gè)可以拿。
所有方法首先都必須獲取到lock的鎖,lock有公平與非公平鎖,默認(rèn)實(shí)現(xiàn)的是非公平鎖,也可以在初始化ArrayBlockingQueue指定,所以默認(rèn)ArrayBlockingQueue并不保證公平性。
notEmpty與notFull都是通過lock創(chuàng)建,都是在初始化ArrayBlockingQueue是初始化出來。
這里簡單介紹了屬性的作用,接下來會(huì)通過源碼再來理解它的作用。
首先要說兩個(gè)私有方法,應(yīng)該隊(duì)列主要的方法最后都依賴這兩個(gè)私有方法,直接看源碼如下圖:
enqueue方法用來把數(shù)據(jù)保存到數(shù)組items中,會(huì)遞增putIndex,也就是下次應(yīng)該保存的位置,如果putIndex等于了數(shù)組的長度,則下次為0。
最后會(huì)喚醒那些調(diào)用notEmpty.await()阻塞的線程,實(shí)際上只有take方法調(diào)用了。
dequeue方法是獲取數(shù)據(jù),獲取的是takeIndex處的數(shù)據(jù),在獲取過后會(huì)把items[takeIndex]處設(shè)置為null,同樣遞增takeIndex后如果等于了數(shù)組的長度則會(huì)被置為0。
最后會(huì)喚醒那些調(diào)用notFull.await()阻塞的線程,只有put方法調(diào)用了。
所以主要的變化在于putIndex和takeIndex,這里總結(jié)了他們有如下4種關(guān)系:
如果不加保護(hù)他們不止這些關(guān)系,比如takeIndex比putIndex跑的快,那么就會(huì)獲取到null值,獲取putIndex比takeIndex多走一個(gè)輪回還多那么就會(huì)出現(xiàn)數(shù)據(jù)被覆蓋,造成數(shù)據(jù)丟失。
只有保證它們是這幾種關(guān)系才能保證數(shù)據(jù)的安全性,避免數(shù)據(jù)被覆蓋或者獲取到null值,并且實(shí)現(xiàn)了FIFO先進(jìn)先出,而隊(duì)列提供的公共方法都必須要保證這種安全。
ArrayBlockingQueue提供了4個(gè)添加方法add、offer、offer(指定阻塞時(shí)間)、put;
add方法調(diào)用的是offer方法,而offer方法先獲取到鎖,再判斷隊(duì)列是否已滿,已滿直接返回false,沒有滿則調(diào)用enqueue保存數(shù)據(jù)。所以add與offer沒有阻塞。
offer還有一個(gè)可以在隊(duì)列已滿情況下阻塞指定時(shí)間(timeout)在嘗試保存的方法,在判斷隊(duì)列已滿的情況會(huì)調(diào)用”nanos = notFull.awaitNanos(nanos);”阻塞線程,一定時(shí)間后如果還是滿的則會(huì)返回false。
put獲取到鎖,如果數(shù)組已滿則調(diào)用notFull.await一直阻塞等待喚醒,喚醒后再次驗(yàn)證是否已滿,沒滿則調(diào)用enqueue,否則再次阻塞。所以向隊(duì)列中加數(shù)據(jù)只有put方法才支持真正的阻塞,保證添加成功,其他方法會(huì)可能保存失敗。
獲取數(shù)據(jù)提供了poll、poll(指定阻塞時(shí)間)、take、peek
poll方法先獲取到鎖,如果count==0則返回null,否則調(diào)用dequeue方法獲取結(jié)果。
poll(指定阻塞時(shí)間)在判斷count==0時(shí)會(huì)先阻塞執(zhí)行時(shí)間,然后再次判斷,如果還是等于0則返回null,如果不等于0則調(diào)用dequeue方法獲取結(jié)果。
take方法先獲取到鎖然后在循環(huán)判斷count是否等于0如果等于0則調(diào)用notEmpty.await()阻塞,否則調(diào)用dequeue獲取結(jié)果,同樣take方法也會(huì)保證一定能拿到數(shù)據(jù),否則會(huì)一直阻塞。
peek是偷看的意思,peek方法在獲取到鎖后直接獲取item[takeIndex]的元素返回,然后不做任何事情,就好像偷偷看下下一次會(huì)獲取哪一個(gè)元素,但是不影響隊(duì)列。
看完上述內(nèi)容,你們掌握如何分析阻塞隊(duì)列ArrayBlockingQueue源碼的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!