亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

JUC的ArrayBlockingQueue怎么實現數據的添加和拿取

發布時間:2021-12-21 10:28:25 來源:億速云 閱讀:305 作者:iii 欄目:開發技術

本篇內容主要講解“JUC的ArrayBlockingQueue怎么實現數據的添加和拿取”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JUC的ArrayBlockingQueue怎么實現數據的添加和拿取”吧!

ArrayBlockingQueue 有以下幾個特點:

  • 由數組實現的有界阻塞隊列,容量一旦創建,后續大小無法修改;

  • 遵照先進先出規則,隊頭拿數據,隊尾取數據;

  • 跟 LinkedBlockingQueue 一樣,隊列滿時,往隊列中 put 數據會被阻塞,隊列空時,往隊列中拿數據會被阻塞;

  • 對數據操作時,共用一把鎖,所以不能同時讀寫操作;

ArrayBlockingQueue 跟 LinkedBlockingQueue 一樣,同樣繼承了 AbstractQueue 實現 BlockingQueue 接口,所以在方法上跟 LinkedBlockingQueue 一樣,所以在這里我們不把方法列出來了,可以去查看前面 LinkedBlockingQueue 的文章~

除了方法之外,在 ArrayBlockingQueue 中,還有兩個比較重要的參數:

/** items index for next take, poll, peek or remove */
// 獲取元素的位置
int takeIndex;
/** items index for next put, offer, or add */
// 新增元素時的數組下標
int putIndex;

由于 ArrayBlockingQueue 底層采用的是數組,結合上面的兩個參數,ArrayBlockingQueue 的整體結構圖大概如下:

JUC的ArrayBlockingQueue怎么實現數據的添加和拿取

ArrayBlockingQueue 有三個構造函數:


public ArrayBlockingQueue(int capacity);
// fair 表示是否為公平鎖
public ArrayBlockingQueue(int capacity, boolean fair);

public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c);

關于構造函數就不多說了,都大同小異,跟 LinkedBlockingQueue 一樣,同樣拿 put()、take() 方法,看看 ArrayBlockingQueue 是如何實現數據的添加和拿取的~

先從put()方法開始,看看 ArrayBlockingQueue 是如何實現的~

public void put(E e) throws InterruptedException {
   Objects.requireNonNull(e);
   // 獲取鎖
   final ReentrantLock lock = this.lock;
   // 設置可重入鎖
   lock.lockInterruptibly();
   try {
       // 當數組隊列存滿時,阻塞等待.....
       while (count == items.length)
           notFull.await();
       // 入隊操作
       enqueue(e);
   } finally {
       // 解鎖
       lock.unlock();
   }
}
// 入隊  
private void enqueue(E e) {
   final Object[] items = this.items;
   // 根據 putIndex 插入到對應的位置即可
   items[putIndex] = e;
   // 設置好下一次插入的位置,如果當前插入的位置是最后一個元素,
   // 那么下一次插入的位置就是隊頭了
   if (++putIndex == items.length) putIndex = 0;
   count++;
   notEmpty.signal();
}

put() 方法的實現并不復雜,代碼也就 20 行左右,我們來拆解一下 put 過程:

  • 1、先獲取鎖,對操作進行加鎖;

  • 2、判斷隊列是否隊滿,如果隊滿,則掛起等待;

  • 3、根據 putIndex 的值,直接將元素插入到 items 數組中;

  • 4、調整 putIndex 的位置,用于下一次插入使用,如果當前 putIndex 是數組的最后一個位置,則 putIndex 下一次插入的位置是數組的第一個位置,可以把它當作是循環;

  • 5、解鎖;

put 方整體解決起來不難,跟 LinkedBlockingQueue 一樣,其他添加方法這里就不介紹了,大同小異~

再來看看 ArrayBlockingQueue 是如何實現 take() 方法的,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;
   @SuppressWarnings("unchecked")
   // 根據 takeIndex 獲取 items 中的元素
   E e = (E) items[takeIndex];
   // 將 takeIndex 中的數據置為空
   items[takeIndex] = null;
   // 設置下一次獲取數據的下標,
   if (++takeIndex == items.length) takeIndex = 0;
   count--;
   if (itrs != null)
       itrs.elementDequeued();
   notFull.signal();
   return e;
}

take() 方法跟 put() 方法沒有什么太大的區別,就是一個反操作~

最后我們再來關注一下 remove() 方法,這個方法還是有一些學問的,主要源碼如下:


public boolean remove(Object o) {
   if (o == null) return false;
   final ReentrantLock lock = this.lock;
   lock.lock();
   try {
       if (count > 0) {
           final Object[] items = this.items;
           for (int i = takeIndex, end = putIndex,
                    to = (i < end) ? end : items.length;
                ; i = 0, to = end) {
                // 遍歷有值的一段數據
               for (; i < to; i++)
                   if (o.equals(items[i])) {
                       removeAt(i);
                       return true;
                   }
               if (to == end) break;
           }
       }
       return false;
   } finally {
       lock.unlock();
   }
}
// 主要看這個方法
void removeAt(final int removeIndex) {
   final Object[] items = this.items;
   // 如果要刪除的位置正好是下一次 take的位置
   if (removeIndex == takeIndex) {
       // removing front item; just advance
       items[takeIndex] = null;
       if (++takeIndex == items.length) takeIndex = 0;
       count--;
       if (itrs != null)
           itrs.elementDequeued();
   } else {
       // 如果刪除的位置時 takeIndex 和 putIndex 之間的位置,則被刪除的數據全部往前移動~
       for (int i = removeIndex, putIndex = this.putIndex;;) {
           int pred = i;
           if (++i == items.length) i = 0;
           if (i == putIndex) {
               items[pred] = null;
               this.putIndex = pred;
               break;
           }
           items[pred] = items[i];
       }
       count--;
       if (itrs != null)
           itrs.removedAt(removeIndex);
   }
   notFull.signal();
}

remove 的時候分三種情況:

  • 第一種:當 removeIndex == takeIndex 時,這種情況就比較簡單,將該位置的元素刪除后,takeIndex +1 即可;

  • 第二種:當 removeIndex + 1 == putIndex 時,直接將 putIndex -1 就好,相當于 putIndex 指針往前移動一格;

  • 第三種:當 removeIndex != takeIndex && removeIndex + 1 != putIndex 時,這種情況就比較復雜了,需要涉及到數據的移動,要將 removeIndex 后面的數據全部往前移動一個位置,putIndex 的位置也要遷移一位,具體的可以參考源碼;

到此,相信大家對“JUC的ArrayBlockingQueue怎么實現數據的添加和拿取”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

黄大仙区| 仁化县| 顺平县| 湘乡市| 平顶山市| 登封市| 永和县| 吉木乃县| 郧西县| 繁昌县| 鄂温| 黔西县| 启东市| 柘城县| 荃湾区| 盱眙县| 珠海市| 龙门县| 西林县| 平塘县| 武隆县| 桂林市| 鹤峰县| 田阳县| 秭归县| 东至县| 临邑县| 都昌县| 务川| 大邑县| 博兴县| 石景山区| 普洱| 抚州市| 大港区| 涟源市| 临海市| 陕西省| 淳安县| 公安县| 美姑县|