Java常見(jiàn)的阻塞隊(duì)列總結(jié)
Java阻塞隊(duì)列
阻塞隊(duì)列和普通隊(duì)列主要區(qū)別在阻塞二字:
- 阻塞添加:隊(duì)列已滿時(shí),添加元素線程會(huì)阻塞,直到隊(duì)列不滿時(shí)才喚醒線程執(zhí)行添加操作
- 阻塞刪除:隊(duì)列元素為空時(shí),刪除元素線程會(huì)阻塞,直到隊(duì)列不為空再執(zhí)行刪除操作
常見(jiàn)的阻塞隊(duì)列有 LinkedBlockingQueue 和 ArrayBlockingQueue,其中它們都實(shí)現(xiàn) BlockingQueue 接口,該接口定義了阻塞隊(duì)列需實(shí)現(xiàn)的核心方法:
public interface BlockingQueue<E> extends Queue<E> {
// 添加元素到隊(duì)尾,成功返回true,隊(duì)列滿拋出異常 IllegalStateException
boolean add(E e);
// 添加元素到隊(duì)尾,成功返回 true,隊(duì)列滿返回 false
boolean offer(E e);
// 阻塞添加
void put(E e) throws InterruptedException;
// 阻塞添加,包含最大等待時(shí)長(zhǎng)
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
// 阻塞移除隊(duì)頂元素
E take() throws InterruptedException;
// 阻塞移除隊(duì)頂元素,包含最大等待時(shí)長(zhǎng)
E poll(long timeout, TimeUnit unit) throws InterruptedException;
// 返回可以添加到隊(duì)列不阻塞的最大數(shù)量
int remainingCapacity();
// 如果存在元素則刪除,成功返回 true,失敗返回 false
boolean remove(Object o);
// 是否包含某元素
public boolean contains(Object o);
// 批量移除元素并添加入指定集合
int drainTo(Collection<? super E> c);
// 批量移除包含最大數(shù)量
int drainTo(Collection<? super E> c, int maxElements);
}
除了上面的方法,還有三個(gè)繼承自 Queue 接口的方法常常被用到:
// 獲取隊(duì)列頭元素,不刪除,沒(méi)有拋出異常 NoSuchElementException E element(); // 獲取隊(duì)列頭元素,不刪除,沒(méi)有返回 null E peek(); // 獲取并移除隊(duì)列頭元素,沒(méi)有返回 nul E poll();
根據(jù)具體作用,方法可以被分為以下三類(lèi):
- 添加元素類(lèi):add() 成功返回 true,失敗拋異常、offer() 成功返回 true,失敗返回 false,可以定義最大等待時(shí)長(zhǎng)、put() 阻塞方法
- 刪除元素類(lèi):remove() 成功返回 true,失敗返回 false、poll() 成功返回被移除元素,為空返回 null、take() 阻塞方法
- 查詢?cè)仡?lèi):element() 成功返回元素,否則拋出異常、peek() 返回對(duì)應(yīng)元素或 null
根據(jù)方法類(lèi)型又可以分為阻塞和非阻塞,其中 put()、take() 是阻塞方法,帶最大等待時(shí)長(zhǎng)的 offer() 和 poll() 也是阻塞方法,其余都是非阻塞方法,阻塞隊(duì)列基于上述方法實(shí)現(xiàn)
ArrayBlockingQueue 基于數(shù)組實(shí)現(xiàn),滿足隊(duì)列先進(jìn)先出特性,下面我們通過(guò)一段代碼初步認(rèn)識(shí):
public class ArrayBlockingQueueTest {
ArrayBlockingQueue<TestProduct> queue = new ArrayBlockingQueue<TestProduct>(1);
public static void main(String[] args) {
ArrayBlockingQueueTest test = new ArrayBlockingQueueTest();
new Thread(test.new Product()).start();
new Thread(test.new Customer()).start();
}
class Product implements Runnable {
@Override
public void run() {
while (true) {
try {
queue.put(new TestProduct());
System.out.println("生產(chǎn)者創(chuàng)建產(chǎn)品等待消費(fèi)者消費(fèi)");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Customer implements Runnable {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
queue.take();
System.out.println("消費(fèi)者消費(fèi)產(chǎn)品等待生產(chǎn)者創(chuàng)建");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TestProduct {
}
}
上述代碼比較簡(jiǎn)單,在一個(gè)容量為1的阻塞隊(duì)列中,生產(chǎn)者和消費(fèi)者由于容量限制依次阻塞運(yùn)行。
ArrayBlockingQueue 基于 ReentrantLock 鎖和 Condition 等待隊(duì)列實(shí)現(xiàn),因此存在公平和非公平的兩種模式。公平場(chǎng)景下所有被阻塞的線程按照阻塞順序執(zhí)行,非公平場(chǎng)景下,隊(duì)列中的線程和恰好準(zhǔn)備進(jìn)入隊(duì)列的線程競(jìng)爭(zhēng),誰(shuí)搶到就是誰(shuí)的。默認(rèn)使用非公平鎖,因?yàn)樾矢撸?/p>
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
從代碼可以看出,ArrayBlockingQueue 通過(guò)一個(gè) ReentrantLock 鎖以及兩個(gè) Condition 等待隊(duì)列實(shí)現(xiàn),它的屬性如下:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
// 保存數(shù)據(jù)的數(shù)組
final Object[] items;
// 移除元素的索引
int takeIndex;
// 添加元素的索引
int putIndex;
// 元素?cái)?shù)量
int count;
// 用于并發(fā)控制的鎖
final ReentrantLock lock;
// 不為空,用于take()操作
private final Condition notEmpty;
// 不滿,用于put()操作
private final Condition notFull;
// 迭代器
transient Itrs itrs = null;
}
從代碼可以看出,ArrayBlockingQueue 使用同一個(gè)鎖、移除元素和添加元素通過(guò)數(shù)組下標(biāo)的方式記錄,分表表示隊(duì)列頭和隊(duì)列尾。通過(guò)兩個(gè)等待隊(duì)列分別阻塞 take() 和 put() 方法,下面我們直接看源碼:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
public boolean offer(E e) {
// 檢查是否為空
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 判斷隊(duì)列是否已滿
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
final Object[] items = this.items;
// 賦值保存數(shù)據(jù)
items[putIndex] = x;
// 循環(huán)復(fù)用空間
if (++putIndex == items.length)
putIndex = 0;
count++;
// 喚醒take線程
notEmpty.signal();
}
從代碼可以看出:add() 方法基于 offer() 方法實(shí)現(xiàn),offer() 方法添加失敗返回 false 后,add() 方法拋出異常。offer() 方法會(huì)加鎖,保證線程安全,隊(duì)列沒(méi)滿時(shí)執(zhí)行入隊(duì)操作,入隊(duì)操作通過(guò)操作數(shù)組實(shí)現(xiàn),并且通過(guò)循環(huán)復(fù)用數(shù)組空間。元素添加成功后隊(duì)列不為空,調(diào)用 signal() 方法喚醒移除元素的阻塞線程,最后我們看 put() 方法:
public void put(E e) throws InterruptedException {
// 判斷不為空
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 隊(duì)列滿就掛起在等待隊(duì)列
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
從代碼可以看出,當(dāng)隊(duì)列滿時(shí),當(dāng)前線程會(huì)被掛起到等待隊(duì)列中,直到隊(duì)列不滿時(shí)被喚醒執(zhí)行添加操作。下面我們看刪除操作:
public boolean remove(Object o) {
// 判斷是否為 NULL
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
// 從移除下標(biāo)開(kāi)始遍歷到添加新元素的下標(biāo)
do {
if (o.equals(items[i])) {
removeAt(i);
return true;
}
// 循環(huán)判斷,移除下標(biāo)可能大于添加下標(biāo)(添加下標(biāo)二次遍歷時(shí))
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
void removeAt(final int removeIndex) {
final Object[] items = this.items;
// 要?jiǎng)h除的元素正好是移除下標(biāo)
if (removeIndex == takeIndex) {
items[takeIndex] = null;
// 循環(huán)刪除
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else {
final int putIndex = this.putIndex;
// 如果不是移除下標(biāo),從該下標(biāo)開(kāi)始到添加下標(biāo),所有元素左移一位
for (int i = removeIndex;;) {
int next = i + 1;
if (next == items.length)
next = 0;
if (next != putIndex) {
// 向左移除
items[i] = items[next];
i = next;
} else {
// 最后put下標(biāo)置為null
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
// 更新迭代器
if (itrs != null)
itrs.removedAt(removeIndex);
}
notFull.signal();
}
remove() 和 poll()、take() 不同,它可以刪除指定的元素。這里需要考慮刪除的元素不是移除索引指向的情況,從代碼可以看出,當(dāng)要?jiǎng)h除的元素不是移除索引指向的元素時(shí),將所有從被刪除元素下標(biāo)開(kāi)始到添加元素下標(biāo)所有元素左移一位。
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
final Object[] items = this.items;
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
// 移除元素后喚醒put()添加線程
notFull.signal();
return x;
}
相比 remove() 方法,poll() 方法簡(jiǎn)單了很多,這里不做贅述,下面我們看 take():
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 隊(duì)列為空就掛起
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
take() 方法和 put() 方法可以說(shuō)基本一致,相對(duì)也比較簡(jiǎn)單,最后我們來(lái)看看兩個(gè)查詢方法:
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 直接返回移除元素下標(biāo)對(duì)應(yīng)的元素,也就是隊(duì)列頭
return itemAt(takeIndex);
} finally {
lock.unlock();
}
}
final E itemAt(int i) {
return (E) items[i];
}
element() 基于 peek() 方法實(shí)現(xiàn)實(shí)現(xiàn)、當(dāng)隊(duì)列為空時(shí),peek() 方法返回 null,element() 拋出異常。關(guān)于 ArrayBlockingQueue 就介紹到這里
LinkedBlockingQueue 基于鏈表實(shí)現(xiàn),它的屬性如下:
public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
// 鏈表節(jié)點(diǎn),存儲(chǔ)元素
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
// 鏈表容量
private final int capacity;
// 當(dāng)前元素?cái)?shù)量
private final AtomicInteger count = new AtomicInteger();
// 頭節(jié)點(diǎn)
transient Node<E> head;
// 尾節(jié)點(diǎn)
private transient Node<E> last;
// 刪除鎖
private final ReentrantLock takeLock = new ReentrantLock();
// 不為空等待隊(duì)列
private final Condition notEmpty = takeLock.newCondition();
// 添加鎖
private final ReentrantLock putLock = new ReentrantLock();
// 不滿等待隊(duì)列
private final Condition notFull = putLock.newCondition();
}
從代碼可以看出,元素被封裝為 Node 節(jié)點(diǎn)保存在單向鏈表中,其中鏈表默認(rèn)長(zhǎng)度為 Integer.MAX_VALUE,因此在使用時(shí)需注意內(nèi)存溢出:當(dāng)添加元素速度大于刪除元素速度時(shí),隊(duì)列最終會(huì)記錄到大量不會(huì)用到并且無(wú)法回收的對(duì)象,導(dǎo)致內(nèi)存溢出。
ArrayBlockingQueue 和 LinkedBlockingQueue 的主要區(qū)別在于 ReentrantLock 鎖的數(shù)量和等待隊(duì)列,LinkedBlockingQueue 用到兩個(gè)鎖和兩個(gè)等待隊(duì)列,也就是說(shuō)添加和刪除操作可以并發(fā)執(zhí)行,整體效率更高。下面我們直接看代碼:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
public boolean offer(E e) {
// 元素為空拋出異常
if (e == null) throw new NullPointerException();
// 獲取當(dāng)前隊(duì)列容量
final AtomicInteger count = this.count;
// 隊(duì)列已滿時(shí)直接返回false
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
// 獲取添加鎖
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
// 二次判斷,因?yàn)樯厦媾袛鄷r(shí)未加鎖,數(shù)據(jù)可能已更新
if (count.get() < capacity) {
// 入隊(duì)操作
enqueue(node);
// 獲取還未添加元素前,隊(duì)列的容量
c = count.getAndIncrement();
if (c + 1 < capacity)
// 喚醒其它添加元素的線程
notFull.signal();
}
} finally {
putLock.unlock();
}
// 如果添加前隊(duì)列沒(méi)有數(shù)據(jù),也就是說(shuō)現(xiàn)在有一條數(shù)據(jù)時(shí)
if (c == 0)
// 喚醒take線程
signalNotEmpty();
return c >= 0;
}
private void enqueue(Node<E> node) {
last = last.next = node;
}
private void signalNotEmpty() {
// 喚醒take線程前必須獲取對(duì)應(yīng)take鎖
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
這里有以下幾點(diǎn)需要我們注意:
1.LinkedBlockingQueue count 屬性必須通過(guò)并發(fā)類(lèi)封裝,因?yàn)榭赡艽嬖谔砑?、刪除兩個(gè)線程并發(fā)執(zhí)行,需考慮同步
2.這里需要判斷兩次的主要原因在于方法開(kāi)始時(shí)并沒(méi)有加鎖,數(shù)值可能改變,因此在獲取到鎖后需要二次判斷
3.和 ArrayBlockingQueue 不同,LinkedBlockingQueue 在隊(duì)列不滿時(shí)會(huì)喚醒添加線程,這樣做的原因是 LinkedBlockingQueue 中添加和刪除操作使用不同的鎖,各自只需管好自己,還可以提高吞吐量。而 ArrayBlockingQueue 使用唯一鎖,這樣做會(huì)導(dǎo)致移除線程永遠(yuǎn)不被喚醒或添加線程永遠(yuǎn)不被喚醒,吞吐量較低
4.添加元素前隊(duì)列長(zhǎng)度為0才喚醒移除線程,因?yàn)殛?duì)列長(zhǎng)度為0時(shí),移除線程肯定已經(jīng)掛起,此時(shí)喚醒一個(gè)移除線程即可。因?yàn)橐瞥€程和添加線程類(lèi)似,都會(huì)自己?jiǎn)拘炎约?。?c>0 時(shí)只會(huì)有兩種情況:存在移除線程在運(yùn)行,如果有會(huì)遞歸喚醒,無(wú)須我們參與、不存在移除線程運(yùn)行,此時(shí)也無(wú)須我們參與,等待調(diào)用 take()、poll() 方法即可
5.喚醒只針對(duì) put()、take() 方法阻塞的線程,offer() 方法直接返回(不包含最大等待時(shí)長(zhǎng)),不參與喚醒場(chǎng)景
下面我們來(lái)看 put() 阻塞方法的實(shí)現(xiàn):
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
// 隊(duì)列滿時(shí)阻塞
while (count.get() == capacity) {
notFull.await();
}
// 入隊(duì)
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
從代碼可以看出,put() 方法和 offer() 方法唯一區(qū)別在于自身通過(guò) condition 阻塞掛起到等待隊(duì)列,其余基本相同。至此關(guān)于添加操作介紹完畢,下面我們看移除方法:
public boolean remove(Object o) {
if (o == null) return false;
// 同時(shí)加兩個(gè)鎖
fullyLock();
try {
// 循環(huán)查找
for (Node<E> trail = head, p = trail.next; p != null; trail = p, p = p.next) {
if (o.equals(p.item)) {
unlink(p, trail);
return true;
}
}
return false;
} finally {
fullyUnlock();
}
}
void unlink(Node<E> p, Node<E> trail) {
// p是要溢出的節(jié)點(diǎn),trail是它的前驅(qū)節(jié)點(diǎn)
// 方便gc
p.item = null;
// 引用取消
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
void fullyLock() {
putLock.lock();
takeLock.lock();
}
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
從代碼可以看出,remove() 方法只會(huì)在操作前容量不滿時(shí)喚醒創(chuàng)建線程,并不會(huì)喚醒移除線程。并且由于我們不確定要?jiǎng)h除元素的位置,因此此時(shí)需要加兩個(gè)鎖,確保數(shù)據(jù)安全。
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = dequeue();
// 獲取移除前隊(duì)列的元素?cái)?shù)量
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
// 移除前如果隊(duì)列是滿的,喚醒添加線程
if (c == capacity)
signalNotFull();
return x;
}
private E dequeue() {
Node<E> h = head;
// 獲取要?jiǎng)h除的節(jié)點(diǎn)
Node<E> first = h.next;
// 清除原來(lái)的頭結(jié)點(diǎn)(方便gc)
h.next = h;
// 設(shè)置新的頭結(jié)點(diǎn)
head = first;
// 獲取返回值
E x = first.item;
// 新頭結(jié)點(diǎn)置為空
first.item = null;
return x;
}
需要注意的一點(diǎn),每次出隊(duì)時(shí)更換 head 節(jié)點(diǎn),head 節(jié)點(diǎn)本身不保存數(shù)據(jù),head.next 記錄下次需要出隊(duì)的元素,每次出隊(duì)后 head.next 變?yōu)樾碌?head 節(jié)點(diǎn)返回并置為 null
poll() 方法和上面提到的 offer() 方法基本鏡像相同,這里我再不做過(guò)多贅述
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 隊(duì)列為空就掛起
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
take() 方法和 poll() 方法類(lèi)似,區(qū)別在于新增了阻塞邏輯。至此關(guān)于溢出元素方法介紹完畢,最后我們看看查詢方法源碼:
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
從代碼可以看出,默認(rèn) head 和 last 頭尾節(jié)點(diǎn)都為 null,入隊(duì)時(shí)直接從 next 開(kāi)始操作,也就是說(shuō) head 節(jié)點(diǎn)不保存數(shù)據(jù)。
最后我們來(lái)看看有最大等待時(shí)長(zhǎng)的 offer() 方法:
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
if (e == null) throw new NullPointerException();
// 將時(shí)間轉(zhuǎn)換成納秒
long nanos = unit.toNanos(timeout);
int c = -1;
// 獲取鎖
final ReentrantLock putLock = this.putLock;
// 獲取當(dāng)前隊(duì)列大小
final AtomicInteger count = this.count;
// 可中斷鎖
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
// 小于0說(shuō)明已到達(dá)最大等待時(shí)長(zhǎng)
if (nanos <= 0)
return false;
// 如果隊(duì)列已滿,根據(jù)等待隊(duì)列阻塞等待
nanos = notFull.awaitNanos(nanos);
}
// 隊(duì)列沒(méi)滿直接入隊(duì)
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
public final long awaitNanos(long nanosTimeout) throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
// 將當(dāng)前線程封裝為 AQS Node 類(lèi)加入等待隊(duì)列
Node node = addConditionWaiter();
// 釋放鎖
int savedState = fullyRelease(node);
//計(jì)算過(guò)期時(shí)間
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
// 當(dāng)前線程沒(méi)有喚醒進(jìn)入同步隊(duì)列時(shí)
while (!isOnSyncQueue(node)) {
// 已經(jīng)等待相應(yīng)時(shí)間,刪除當(dāng)前節(jié)點(diǎn),將狀態(tài)設(shè)置為已關(guān)閉從隊(duì)列刪除
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
// 判斷是否超時(shí)
if (nanosTimeout >= spinForTimeoutThreshold)
// 掛起線程
LockSupport.parkNanos(this, nanosTimeout);
// 判斷線程狀態(tài)是否被中斷
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
// 重新計(jì)算剩余等待時(shí)間
nanosTimeout = deadline - System.nanoTime();
}
// 被喚醒后執(zhí)行自旋操作爭(zhēng)取獲得鎖,同時(shí)判斷線程是否被中斷
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
// 清理等待隊(duì)列中不為Condition狀態(tài)的線程
unlinkCancelledWaiters();
// 判斷是否被中斷
if (interruptMode != 0)
// 拋出異?;蛑袛嗑€程,獨(dú)占模式拋出異常,共享模式中斷線程
reportInterruptAfterWait(interruptMode);
// 返回時(shí)差,如果成功當(dāng)前時(shí)間小于最大等待時(shí)長(zhǎng),返回值大于0,否則返回值小于0
return deadline - System.nanoTime();
}
從代碼可以看出,包含最大等待時(shí)長(zhǎng)的 offer()、poll() 方法通過(guò)循環(huán)判斷時(shí)間是否超時(shí)的方式掛起在等待隊(duì)列,達(dá)到最大等待時(shí)長(zhǎng)還未被喚醒或沒(méi)被執(zhí)行就返回
ArrayBlockingQueue 和 LinkedBlockingQueue 對(duì)比:
- 大小不同,一個(gè)有界,一個(gè)無(wú)界。ArrayBlockingQueue 必須指定初始大小,LinkedBlockingQueue 無(wú)界時(shí)可能內(nèi)存溢出
- 一個(gè)采用數(shù)組,一個(gè)采用鏈表,數(shù)組保存無(wú)須創(chuàng)建新對(duì)象,鏈表需創(chuàng)建 Node 對(duì)象
- 鎖機(jī)制不同,ArrayBlockingQueue 添加刪除操作使用同一個(gè)鎖,兩者操作不能并發(fā)執(zhí)行。LinkedBlockingQueue 添加和刪除使用不同鎖,添加和刪除操作可并發(fā)執(zhí)行,整體效率 LinkedBlockingQueue 更高
到此這篇關(guān)于Java常見(jiàn)的阻塞隊(duì)列總結(jié)的文章就介紹到這了,更多相關(guān)Java阻塞隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目多層級(jí)多環(huán)境yml設(shè)計(jì)詳解
這篇文章主要為大家介紹了SpringBoot項(xiàng)目多層級(jí)多環(huán)境yml設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
java快速解析路徑中的參數(shù)(&與=拼接的參數(shù))
這篇文章主要介紹了java快速解析路徑中的參數(shù)(&與=拼接的參數(shù)),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-02-02
SpringMVC實(shí)現(xiàn)文件上傳和下載的工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了SpringMVC實(shí)現(xiàn)文件上傳和下載的工具類(lèi),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Java如何實(shí)現(xiàn)微信支付v3的支付回調(diào)
這篇文章主要給大家介紹了關(guān)于Java如何實(shí)現(xiàn)微信支付v3的支付回調(diào),微信實(shí)現(xiàn)支付功能與支付寶實(shí)現(xiàn)支付功能是相似的,文中給了詳細(xì)的示例代碼,需要的朋友可以參考下2023-07-07
Java實(shí)現(xiàn)圖片與Base64編碼互轉(zhuǎn)
這篇文章主要介紹了Java中實(shí)現(xiàn)圖片與Base64編碼互轉(zhuǎn)的方法,比較實(shí)用,需要的朋友可以參考下。2016-06-06
Spring boot實(shí)現(xiàn)熱部署的兩種方式詳解
這篇文章主要介紹了Spring boot實(shí)現(xiàn)熱部署的兩種方式,這兩種方法分別是使用 Spring Loaded和使用spring-boot-devtools進(jìn)行熱部署,文中給出了詳細(xì)示例代碼和介紹,需要的朋友可以參考學(xué)習(xí),下面來(lái)一起看看吧。2017-04-04
idea hibernate jpa 生成實(shí)體類(lèi)的實(shí)現(xiàn)
這篇文章主要介紹了idea hibernate jpa 生成實(shí)體類(lèi)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Spring-cloud-eureka使用feign調(diào)用服務(wù)接口
這篇文章主要為大家詳細(xì)介紹了Spring-cloud-eureka使用feign調(diào)用服務(wù)接口,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
Spring?RestTemplate遠(yuǎn)程調(diào)用過(guò)程
這篇文章主要介紹了Spring?RestTemplate遠(yuǎn)程調(diào)用過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11

