溫馨提示×

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

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

JUC的ArrayBlockingQueue怎么實(shí)現(xiàn)數(shù)據(jù)的添加和拿取

發(fā)布時(shí)間:2021-12-21 10:28:25 來(lái)源:億速云 閱讀:296 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“JUC的ArrayBlockingQueue怎么實(shí)現(xiàn)數(shù)據(jù)的添加和拿取”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“JUC的ArrayBlockingQueue怎么實(shí)現(xiàn)數(shù)據(jù)的添加和拿取”吧!

ArrayBlockingQueue 有以下幾個(gè)特點(diǎn):

  • 由數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,容量一旦創(chuàng)建,后續(xù)大小無(wú)法修改;

  • 遵照先進(jìn)先出規(guī)則,隊(duì)頭拿數(shù)據(jù),隊(duì)尾取數(shù)據(jù);

  • 跟 LinkedBlockingQueue 一樣,隊(duì)列滿時(shí),往隊(duì)列中 put 數(shù)據(jù)會(huì)被阻塞,隊(duì)列空時(shí),往隊(duì)列中拿數(shù)據(jù)會(huì)被阻塞;

  • 對(duì)數(shù)據(jù)操作時(shí),共用一把鎖,所以不能同時(shí)讀寫操作;

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

除了方法之外,在 ArrayBlockingQueue 中,還有兩個(gè)比較重要的參數(shù):

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

由于 ArrayBlockingQueue 底層采用的是數(shù)組,結(jié)合上面的兩個(gè)參數(shù),ArrayBlockingQueue 的整體結(jié)構(gòu)圖大概如下:

JUC的ArrayBlockingQueue怎么實(shí)現(xiàn)數(shù)據(jù)的添加和拿取

ArrayBlockingQueue 有三個(gè)構(gòu)造函數(shù):


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

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

關(guān)于構(gòu)造函數(shù)就不多說(shuō)了,都大同小異,跟 LinkedBlockingQueue 一樣,同樣拿 put()、take() 方法,看看 ArrayBlockingQueue 是如何實(shí)現(xiàn)數(shù)據(jù)的添加和拿取的~

先從put()方法開始,看看 ArrayBlockingQueue 是如何實(shí)現(xiàn)的~

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

put() 方法的實(shí)現(xiàn)并不復(fù)雜,代碼也就 20 行左右,我們來(lái)拆解一下 put 過(guò)程:

  • 1、先獲取鎖,對(duì)操作進(jìn)行加鎖;

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

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

  • 4、調(diào)整 putIndex 的位置,用于下一次插入使用,如果當(dāng)前 putIndex 是數(shù)組的最后一個(gè)位置,則 putIndex 下一次插入的位置是數(shù)組的第一個(gè)位置,可以把它當(dāng)作是循環(huán);

  • 5、解鎖;

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

再來(lái)看看 ArrayBlockingQueue 是如何實(shí)現(xiàn) take() 方法的,take() 方法主要源碼如下:


public E take() throws InterruptedException {
   // 獲取鎖
   final ReentrantLock lock = this.lock;
   lock.lockInterruptibly();
   try {
       // 判斷隊(duì)列是否為空,為空的話掛起等待
       while (count == 0)
           notEmpty.await();
       // 獲取數(shù)據(jù)
       return dequeue();
   } finally {
       lock.unlock();
   }
}
private E dequeue() {
   
   final Object[] items = this.items;
   @SuppressWarnings("unchecked")
   // 根據(jù) takeIndex 獲取 items 中的元素
   E e = (E) items[takeIndex];
   // 將 takeIndex 中的數(shù)據(jù)置為空
   items[takeIndex] = null;
   // 設(shè)置下一次獲取數(shù)據(jù)的下標(biāo),
   if (++takeIndex == items.length) takeIndex = 0;
   count--;
   if (itrs != null)
       itrs.elementDequeued();
   notFull.signal();
   return e;
}

take() 方法跟 put() 方法沒(méi)有什么太大的區(qū)別,就是一個(gè)反操作~

最后我們?cè)賮?lái)關(guān)注一下 remove() 方法,這個(gè)方法還是有一些學(xué)問(wèn)的,主要源碼如下:


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) {
                // 遍歷有值的一段數(shù)據(jù)
               for (; i < to; i++)
                   if (o.equals(items[i])) {
                       removeAt(i);
                       return true;
                   }
               if (to == end) break;
           }
       }
       return false;
   } finally {
       lock.unlock();
   }
}
// 主要看這個(gè)方法
void removeAt(final int removeIndex) {
   final Object[] items = this.items;
   // 如果要?jiǎng)h除的位置正好是下一次 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 {
       // 如果刪除的位置時(shí) takeIndex 和 putIndex 之間的位置,則被刪除的數(shù)據(jù)全部往前移動(dòng)~
       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 的時(shí)候分三種情況:

  • 第一種:當(dāng) removeIndex == takeIndex 時(shí),這種情況就比較簡(jiǎn)單,將該位置的元素刪除后,takeIndex +1 即可;

  • 第二種:當(dāng) removeIndex + 1 == putIndex 時(shí),直接將 putIndex -1 就好,相當(dāng)于 putIndex 指針往前移動(dòng)一格;

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

到此,相信大家對(duì)“JUC的ArrayBlockingQueue怎么實(shí)現(xiàn)數(shù)據(jù)的添加和拿取”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

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

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

AI