不允許還有Java程序員不了解BlockingQueue阻塞隊列的實現原理( 二 )


4.2 put方法源碼解析

不允許還有Java程序員不了解BlockingQueue阻塞隊列的實現原理

文章插圖
無論是放數據還是取數據都是從隊頭開始,逐漸往隊尾移動 。
// 放數據 , 如果隊列已滿 , 就一直阻塞,直到有其他線程從隊列中取走數據public void put(E e) throws InterruptedException {// 校驗元素不能為空checkNotNull(e);final ReentrantLock lock = this.lock;// 加鎖,加可中斷的鎖lock.lockInterruptibly();try {// 如果隊列已滿,就一直阻塞,直到被喚醒while (count == items.length)notFull.await();// 如果隊列未滿 , 就往隊列添加元素enqueue(e);} finally {// 結束后,別忘了釋放鎖lock.unlock();}}// 實際往隊列添加數據的方法private void enqueue(E x) {// 獲取數組final Object[] items = this.items;// putIndex 表示本次插入的位置items[putIndex] = x;// ++putIndex 計算下次插入的位置// 如果本次插入的位置,正好是隊尾,下次插入就從 0 開始if (++putIndex == items.length)putIndex = 0;// 元素數量加一count++;// 喚醒因為隊列空等待的線程notEmpty.signal();}源碼中有個有意思的設計,添加元素的時候如果已經到了隊尾,下次就從隊頭開始添加,相當于做成了一個循環隊列 。
像下面這樣:
不允許還有Java程序員不了解BlockingQueue阻塞隊列的實現原理

文章插圖
4.3 take方法源碼// 取數據 , 如果隊列為空,就一直阻塞 , 直到有其他線程往隊列中放數據public E take() throws InterruptedException {final ReentrantLock lock = this.lock;// 加鎖,加可中斷的鎖lock.lockInterruptibly();try {// 如果隊列為空 , 就一直阻塞,直到被喚醒while (count == 0)notEmpty.await();// 如果隊列不為空,就從隊列取數據return dequeue();} finally {// 結束后,別忘了釋放鎖lock.unlock();}}// 實際從隊列取數據的方法private E dequeue() {// 獲取數組final Object[] items = this.items;// takeIndex 表示本次取數據的位置 , 是上一次取數據時計算好的E x = (E) items[takeIndex];// 取完之后,就把隊列該位置的元素刪除items[takeIndex] = null;// ++takeIndex 計算下次取數據的位置// 如果本次取數據的位置,正好是隊尾,下次就從 0 開始取數據if (++takeIndex == items.length)takeIndex = 0;// 元素數量減一count--;if (itrs != null)itrs.elementDequeued();// 喚醒被隊列滿所阻塞的線程notFull.signal();return x;}4.4 總結
  1. ArrayBlockingQueue基于數組實現的阻塞隊列,創建隊列時需指定容量大?。?是有界隊列 。
  2. ArrayBlockingQueue底層采用循環隊列的形式,保證數組位置可以重復使用 。
  3. ArrayBlockingQueue存取都采用ReentrantLock加鎖,保證線程安全,在多線程環境下也可以放心使用 。
  4. 使用ArrayBlockingQueue的時候,預估好隊列長度,保證生產者和消費者速率相匹配 。
我是「一燈架構」,如果本文對你有幫助 , 歡迎各位小伙伴點贊、評論和關注,感謝各位老鐵,我們下期見

不允許還有Java程序員不了解BlockingQueue阻塞隊列的實現原理

文章插圖

推薦閱讀