您好,登錄后才能下訂單哦!
本篇內(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)圖大概如下:
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í)!
免責(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)容。