溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比

發(fā)布時間:2021-12-23 17:14:00 來源:億速云 閱讀:132 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

今天學(xué)習(xí)LinkedBlockingQueue源碼并與之對比。

LinkedBlockingQueue總結(jié)

同樣先直接總結(jié)LinkedBlockingQueue相關(guān)的特性,再根據(jù)源碼來進(jìn)行說明,它的主要特性如下:

1、他底層用鏈表實現(xiàn)數(shù)據(jù)存儲;

2、他是一個FIFO無界阻塞隊列。數(shù)據(jù)最大長度默認(rèn)Intenger的最大值Integer.MAX_VALUE,也可以設(shè)置長度;

重要屬性介紹

主要屬性如下圖:

 如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比

LinkedBlockingQueue有一個內(nèi)部類Node,用來組成它存儲數(shù)據(jù)的鏈表,Node的item數(shù)據(jù)用來存儲數(shù)據(jù),next表示下一個節(jié)點,尾節(jié)點的next是null。

capacity容量表示隊列最多可存儲的數(shù)據(jù)量,初始化的時候設(shè)置,默認(rèn)是Integer.MAX_VALUE;

count表示當(dāng)前存儲數(shù)據(jù)的數(shù)量,它是AtomicInteger類型(可以想想為什么?);

head鏈表的頭節(jié)點,它的item不存放數(shù)據(jù);

tail鏈表的尾節(jié)點,它的next為null,也就是沒有下一個節(jié)點;

takeLock與notEmpty控制take方法的阻塞;

putLock與notFull控制put方法的阻塞; 

從上面屬性可以猜出來LinkedBlockingQueue是用兩個鎖來控制數(shù)據(jù)的讀和寫,相當(dāng)于把讀和寫分開,可以同時存在讀和寫操作,所以count有可能同時存在多個線程修改,有線程安全問題所以用AtomicInteger。

最關(guān)鍵的兩個私有方法

同樣LinkedBlockingQueue也有兩個相同的關(guān)鍵方法,具體代碼和解釋如下圖:

 如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比

LinkedBlockingQueue的enqueue和dequeue方法比ArrayBlockingQueue的更加簡單;

enqueue方法就是把新的節(jié)點放到last的next,然后把節(jié)點設(shè)置成尾節(jié)點。

dequeue是先拿到頭節(jié)點next節(jié)點作為first節(jié)點,然后之前的頭節(jié)點就的next設(shè)置成了自己,就脫離了鏈表,下次GC就會被回收掉。

然后first就作為head,把item拿出來后設(shè)置為null,所以頭節(jié)點是不存儲數(shù)據(jù)的,并且是由下一個節(jié)點升級來的。 

LinkedBlockingQueue的這兩個方法比較簡單,但是也可以看出它實現(xiàn)了FIFO先進(jìn)先出。

主要方法介紹

LinkedBlockingQueue與ArrayBlockingQueue一樣都是繼承至 AbstractQueue并實現(xiàn) BlockingQueue接口,所以他們提供的方法都差不多,功能也類似,主要實現(xiàn)不同,上一篇已經(jīng)詳細(xì)講了,這里就只看一下take和put方法的實現(xiàn)。

put方法源碼如下圖:

 如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比

一共分四步:

把數(shù)據(jù)封裝成Node節(jié)點,獲取put鎖;

驗證隊列是否已滿,滿則阻塞線程;

如果沒有滿則把節(jié)點加到隊列中,count加1,并獲取加之前的數(shù)量;

如果現(xiàn)在數(shù)量還是小于最大容量則喚醒阻塞的put線程,如果之前數(shù)量是0則喚醒take線程; 

take方法源碼如下圖:

 如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比

take方法和put方法差不多,主要分以下四步:

首先獲取take鎖;

判斷隊列中是否有數(shù)據(jù),如果沒有則阻塞線程;

調(diào)用dequeue方法獲取數(shù)據(jù),并把count減1;

如果獲取前隊列數(shù)量大于1則說明還有數(shù)據(jù),可以喚醒其他take線程,如果獲取前隊列數(shù)量等于最大容器數(shù)量則說明有可能有阻塞的put線程,需要喚醒; 

可以看到put和take方法很簡單,都是先獲取到對于的鎖,然后根據(jù)隊列數(shù)量判斷是否阻塞,然后再添加或者獲取數(shù)據(jù),最后根據(jù)喚醒對應(yīng)線程

與ArrayBlockingQueue對比(重點)

LinkedBlockingQueue與ArrayBlockingQueue是非常相似的類,通過對比能夠更體現(xiàn)他們的優(yōu)缺點,主要對比如下:

從基礎(chǔ)屬性對比LinkedBlockingQueue底層實現(xiàn)是鏈表,ArrayBlockingQueue是數(shù)組。ArrayBlockingQueue初始化需要一個數(shù)組,而LinkedBlockingQueue是動態(tài)數(shù)量的Node對象;所以ArrayBlockingQueue需要預(yù)先分配內(nèi)存,而LinkedBlockingQueue不用,如果預(yù)計需要緩存的數(shù)據(jù)很多的,那么ArrayBlockingQueue一開始就需要很大一塊數(shù)據(jù)

但是ArrayBlockingQueue添加元素更快,因為它只是要要保存的對象的引用放到數(shù)組對應(yīng)位置,而LinkedBlockingQueue需要創(chuàng)建一個Node對象;同時在獲取后這個Node對象變成了垃圾,在讀寫很大的情況會多出很多垃圾,可能會影響程序的性能; 

ArrayBlockingQueue用一個鎖控制讀寫,LinkedBlockingQueue用兩個鎖分別控制讀寫。因為ArrayBlockingQueue為了避免數(shù)據(jù)被覆蓋,而LinkedBlockingQueue不用擔(dān)心這種情況,可以同時支持讀寫,而ArrayBlockingQueue同一時刻支持一個操作,所以LinkedBlockingQueue吞吐量更高。 

所以ArrayBlockingQueue會占用固定的內(nèi)存,所以不能支持太大的緩存,但是每次操作延遲更加低,而LinkedBlockingQueue不用暫用一個初始化的內(nèi)存,但是每次操作消耗的內(nèi)存更大,不過同時支持讀寫所以吞吐量更高

但是在使用LinkedBlockingQueue時,如果使用默認(rèn)大小且當(dāng)生產(chǎn)速度大于消費速度時候,有可能會內(nèi)存溢出。

LinkedBlockingQueue與ArrayBlockingQueue提供的功能完全相同,但是他們底層的實現(xiàn)不同,所以側(cè)重點不同,在使用中可以根據(jù)情況選擇。 

關(guān)于如何進(jìn)行阻塞隊列LinkedBlockingQueue源碼學(xué)習(xí)與對比就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI