您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何進行阻塞隊列LinkedBlockingQueue源碼學習與對比,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
今天學習LinkedBlockingQueue源碼并與之對比。
同樣先直接總結LinkedBlockingQueue相關的特性,再根據源碼來進行說明,它的主要特性如下:
1、他底層用鏈表實現數據存儲;
2、他是一個FIFO無界阻塞隊列。數據最大長度默認Intenger的最大值Integer.MAX_VALUE,也可以設置長度;
主要屬性如下圖:
LinkedBlockingQueue有一個內部類Node,用來組成它存儲數據的鏈表,Node的item數據用來存儲數據,next表示下一個節點,尾節點的next是null。
capacity容量表示隊列最多可存儲的數據量,初始化的時候設置,默認是Integer.MAX_VALUE;
count表示當前存儲數據的數量,它是AtomicInteger類型(可以想想為什么?);
head鏈表的頭節點,它的item不存放數據;
tail鏈表的尾節點,它的next為null,也就是沒有下一個節點;
takeLock與notEmpty控制take方法的阻塞;
putLock與notFull控制put方法的阻塞;
從上面屬性可以猜出來LinkedBlockingQueue是用兩個鎖來控制數據的讀和寫,相當于把讀和寫分開,可以同時存在讀和寫操作,所以count有可能同時存在多個線程修改,有線程安全問題所以用AtomicInteger。
同樣LinkedBlockingQueue也有兩個相同的關鍵方法,具體代碼和解釋如下圖:
LinkedBlockingQueue的enqueue和dequeue方法比ArrayBlockingQueue的更加簡單;
enqueue方法就是把新的節點放到last的next,然后把節點設置成尾節點。
dequeue是先拿到頭節點next節點作為first節點,然后之前的頭節點就的next設置成了自己,就脫離了鏈表,下次GC就會被回收掉。
然后first就作為head,把item拿出來后設置為null,所以頭節點是不存儲數據的,并且是由下一個節點升級來的。
LinkedBlockingQueue的這兩個方法比較簡單,但是也可以看出它實現了FIFO先進先出。
LinkedBlockingQueue與ArrayBlockingQueue一樣都是繼承至 AbstractQueue并實現 BlockingQueue接口,所以他們提供的方法都差不多,功能也類似,主要實現不同,上一篇已經詳細講了,這里就只看一下take和put方法的實現。
put方法源碼如下圖:
一共分四步:
把數據封裝成Node節點,獲取put鎖;
驗證隊列是否已滿,滿則阻塞線程;
如果沒有滿則把節點加到隊列中,count加1,并獲取加之前的數量;
如果現在數量還是小于最大容量則喚醒阻塞的put線程,如果之前數量是0則喚醒take線程;
take方法源碼如下圖:
take方法和put方法差不多,主要分以下四步:
首先獲取take鎖;
判斷隊列中是否有數據,如果沒有則阻塞線程;
調用dequeue方法獲取數據,并把count減1;
如果獲取前隊列數量大于1則說明還有數據,可以喚醒其他take線程,如果獲取前隊列數量等于最大容器數量則說明有可能有阻塞的put線程,需要喚醒;
可以看到put和take方法很簡單,都是先獲取到對于的鎖,然后根據隊列數量判斷是否阻塞,然后再添加或者獲取數據,最后根據喚醒對應線程;
LinkedBlockingQueue與ArrayBlockingQueue是非常相似的類,通過對比能夠更體現他們的優缺點,主要對比如下:
從基礎屬性對比LinkedBlockingQueue底層實現是鏈表,ArrayBlockingQueue是數組。ArrayBlockingQueue初始化需要一個數組,而LinkedBlockingQueue是動態數量的Node對象;所以ArrayBlockingQueue需要預先分配內存,而LinkedBlockingQueue不用,如果預計需要緩存的數據很多的,那么ArrayBlockingQueue一開始就需要很大一塊數據。
但是ArrayBlockingQueue添加元素更快,因為它只是要要保存的對象的引用放到數組對應位置,而LinkedBlockingQueue需要創建一個Node對象;同時在獲取后這個Node對象變成了垃圾,在讀寫很大的情況會多出很多垃圾,可能會影響程序的性能;
ArrayBlockingQueue用一個鎖控制讀寫,LinkedBlockingQueue用兩個鎖分別控制讀寫。因為ArrayBlockingQueue為了避免數據被覆蓋,而LinkedBlockingQueue不用擔心這種情況,可以同時支持讀寫,而ArrayBlockingQueue同一時刻支持一個操作,所以LinkedBlockingQueue吞吐量更高。
所以ArrayBlockingQueue會占用固定的內存,所以不能支持太大的緩存,但是每次操作延遲更加低,而LinkedBlockingQueue不用暫用一個初始化的內存,但是每次操作消耗的內存更大,不過同時支持讀寫所以吞吐量更高。
但是在使用LinkedBlockingQueue時,如果使用默認大小且當生產速度大于消費速度時候,有可能會內存溢出。
LinkedBlockingQueue與ArrayBlockingQueue提供的功能完全相同,但是他們底層的實現不同,所以側重點不同,在使用中可以根據情況選擇。
關于如何進行阻塞隊列LinkedBlockingQueue源碼學習與對比就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。