LinkedBlockingQueue原始碼解析(3)
此文已由作者趙計剛授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
4.3、public E take() throws InterruptedException
原理:
將隊頭元素出隊,如果佇列空了,一直阻塞,直到佇列不為空或者執行緒被中斷
使用方法:
try { abq.take(); } catch (InterruptedException e) { e.printStackTrace(); }
原始碼:
/** * 出隊: * 如果佇列空了,一直阻塞,直到佇列不為空或者執行緒被中斷 */ public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count;//獲取佇列中的元素總量 final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly();//獲取出隊鎖 try { while (count.get() == 0) {//如果沒有元素,一直阻塞 /* * 加入等待佇列, 一直等待條件notEmpty(即被其他執行緒喚醒) * (喚醒其實就是,有執行緒將一個元素入隊了,然後呼叫notEmpty.signal()喚醒其他等待這個條件的執行緒,同時佇列也不空了) */ notEmpty.await(); } x = dequeue();//出隊 c = count.getAndDecrement();//元素數量-1 if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; }
總結:
1、具體入隊與出隊的原理圖:
圖中每一個節點前半部分表示封裝的資料x,後邊的表示指向的下一個引用。
1.1、初始化
初始化之後,初始化一個數據為null,且head和last節點都是這個節點。
1.2、入隊兩個元素過後
這個可以根據入隊方法enqueue(E x)來看,原始碼再貼一遍:
/** * 建立一個節點,並加入連結串列尾部 * * @param x */ private void enqueue(E x) { /* * 封裝新節點,並賦給當前的最後一個節點的下一個節點,然後在將這個節點設為最後一個節點 */ last = last.next = new Node<E>(x); }
其實這我們就可以發現其實真正意義上出隊的頭節點是Head節點的下一個節點。(這也就是Node這個內部類中對next的註釋,我沒有翻譯)
1.3、出隊一個元素後
表面上看,只是將頭節點的next指標指向了要刪除的x1.next,事實上這樣我覺的就完全可以,但是jdk實際上是將原來的head節點刪除了,而上邊看到的這個head節點,正是剛剛出隊的x1節點,只是其值被置空了。
這一塊對應著原始碼來看:dequeue()
/** * 從佇列頭部移除一個節點 */ private E dequeue() { Node<E> h = head;// 獲取頭節點:x==null Node<E> first = h.next;// 將頭節點的下一個節點賦值給first h.next = h; // 將當前將要出隊的節點置null(為了使其做head節點做準備) head = first;// 將當前將要出隊的節點作為了頭節點 E x = first.item;// 獲取出隊節點的值 first.item = null;// 將出隊節點的值置空 return x; }
2、三種入隊對比:
offer(E e):如果佇列沒滿,立即返回true; 如果佇列滿了,立即返回false-->不阻塞
put(E e):如果佇列滿了,一直阻塞,直到佇列不滿了或者執行緒被中斷-->阻塞
offer(E e, long timeout, TimeUnit unit):在隊尾插入一個元素,,如果佇列已滿,則進入等待,直到出現以下三種情況:-->阻塞
被喚醒
等待時間超時
當前執行緒被中斷
3、三種出隊對比:
poll():如果沒有元素,直接返回null;如果有元素,出隊
take():如果佇列空了,一直阻塞,直到佇列不為空或者執行緒被中斷-->阻塞
poll(long timeout, TimeUnit unit):如果佇列不空,出隊;如果佇列已空且已經超時,返回null;如果佇列已空且時間未超時,則進入等待,直到出現以下三種情況:
被喚醒
等待時間超時
當前執行緒被中斷
4、ArrayBlockingQueue與LinkedBlockingQueue對比
ArrayBlockingQueue:
一個物件陣列+一把鎖+兩個條件
入隊與出隊都用同一把鎖
在只有入隊高併發或出隊高併發的情況下,因為運算元組,且不需要擴容,效能很高
採用了陣列,必須指定大小,即容量有限
LinkedBlockingQueue:
一個單向連結串列+兩把鎖+兩個條件
兩把鎖,一把用於入隊,一把用於出隊,有效的避免了入隊與出隊時使用一把鎖帶來的競爭。
在入隊與出隊都高併發的情況下,效能比ArrayBlockingQueue高很多
採用了連結串列,最大容量為整數最大值,可看做容量無限
兩個疑問:
入隊時:c==0是怎樣出現的?
出隊時:c==capcity是怎樣出現的?
這兩個疑問,都是基於對於AtomicInteger的不熟,不明白LinkedBlockingQueue引用的這兩個方法(getAndIncrement和getAndDecrement)先返回舊值還是新值,關於AtomicInteger的原始碼介紹,請檢視《第十一章 AtomicInteger原始碼解析》,具體連結如下:
http://www.cnblogs.com/java-zhao/p/5140158.html
免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐
更多網易技術、產品、運營經驗分享請點選。
相關文章:
【推薦】 網易雲terraform實踐
【推薦】 MySQL多執行緒備份工具mydumper
【推薦】 Redux其實很簡單(原理篇)