您好,登錄后才能下訂單哦!
本篇內容主要講解“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 的整體結構圖大概如下:
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怎么實現數據的添加和拿取”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。