BlockingQueue的作用以及實現的幾個常用阻塞佇列原理
1、BlockingQueue
BlockingQueue為阻塞佇列,其與普通佇列不同的是,以put和get為例,阻塞佇列在put時,若列表滿了,則會等待直到佇列可以加入元素;而阻塞佇列在get時候,若列表為空,則會等待到佇列非空。可以用來解決 生產者消費者問題。
2、ArrayBlockingQueue
阻塞佇列在add時,若列表滿了,則丟擲異常;
阻塞佇列在offer時根據不同引數決定;
阻塞佇列在put時,若列表滿了,則會等待直到佇列可以加入元素;
阻塞佇列在get時候,若列表為空,則會等待到佇列非空。
特點:通過一個lock進行實現,存取共用一個鎖,並且是先進先出FIFO;
原始碼中該阻塞佇列提供了3個建構函式:
// 通過指定佇列的大小
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
// 佇列大小 以及 是否建立公平鎖 true為公平鎖 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();
}
// 將佇列c元素都加入初始化的佇列中
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
從上面可以瞭解到,ArrayBlockQueue是指定大小的,並且是通過ReentrantLock(實現公平鎖和非公平鎖)來進行鎖定
ArrayBlockQueue的add(e)實際就是呼叫了offer(e)
// 根據程式碼就是獲取到ReentranLock 通過加鎖和解鎖 來實現
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
// 加入到佇列中,putIndex 在每次執行完後都回加一,即表示下次放入陣列中的元素
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
// 標記表示該佇列非空,等待中的消費者可以進行執行
notEmpty.signal();
}
// offer中有一個方法為在指定時間內,若佇列滿則等等
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
// put則為一直等待 知道佇列非滿
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
阻塞佇列的take
// 從阻塞佇列中獲取元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 獲取鎖,若中斷則獲取 資料失敗
lock.lockInterruptibly();
try {
// 若沒有元素則等待 則到notEmpty條件被清除notEmpty.signal();
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
// 從佇列中取出元素takeIndex 與putIndex類似初始值為0,遍歷完一次take後從0從新開始,遵循先進先出
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();//同時更新迭代器中的元素資料
// 置為非滿佇列
notFull.signal();
return x;
}
3、LinkedBlockingQueue
private final ReentrantLock putLock = new ReentrantLock();寫入元素的鎖指定為非公平鎖
/** Wait queue for waiting puts */ private final Condition notFull = putLock.newCondition();
private final ReentrantLock takeLock = new ReentrantLock(); 取出元素的鎖,被指定為非公平鎖。
/** Wait queue for waiting takes */ private final Condition notEmpty = takeLock.newCondition();
特點:通過存取鎖分別用兩個鎖,都為非公平鎖,(可以有效的防止共用一個鎖時,一直被寫鎖lock而讀鎖一直得不到鎖的情況)文中以讀鎖和寫鎖相稱:
同樣通過構造器檢視:
// 生成一個沒有大小限制的連結串列
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// 指定大小的連結串列
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
// 將其他佇列中的元素加入該連結串列
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
加入元素的方法:
// 加入元素
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;//判斷是否為指定容器大小,使用原子包裝,這樣可以獲取實施的count,防止被take了這裡卻還未減少數量
if (count.get() == capacity) // 若等於容器則返回false
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;// 獲取寫鎖並進行鎖定
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
// 加入佇列只需要使其下一個節點設值
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
// 獲取讀鎖,並將非空條件去除
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
// put方法
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
while (count.get() == capacity) {
notFull.await();//若滿則釋放寫鎖,並等待
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
取出元素:
// 取出元素 鎖定讀鎖,
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
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;
}
// 標記為佇列非滿狀態
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
4、待續。。。
相關文章
- Java 阻塞佇列(BlockingQueue)的內部實現原理Java佇列BloC
- 阻塞佇列 BlockingQueue佇列BloC
- 阻塞佇列BlockingQueue(三)--DelayQueue佇列BloC
- Java併發包原始碼學習系列:阻塞佇列BlockingQueue及實現原理分析Java原始碼佇列BloC
- Java併發系列 — 阻塞佇列(BlockingQueue)Java佇列BloC
- Java執行緒(篇外篇):阻塞佇列BlockingQueueJava執行緒佇列BloC
- Java中常用七個阻塞佇列的總結Java佇列
- 自己動手實現一個阻塞佇列佇列
- 阻塞佇列 BlockingQueue(二)--ArrayBlockingQueue與LinkedBlockingQueue佇列BloC
- Java BlockingQueue 阻塞佇列[用於多執行緒]JavaBloC佇列執行緒
- 阻塞佇列一——java中的阻塞佇列佇列Java
- Java阻塞佇列中的異類,SynchronousQueue底層實現原理剖析Java佇列
- Java併發指南11:解讀 Java 阻塞佇列 BlockingQueueJava佇列BloC
- 佇列、阻塞佇列佇列
- 從0到1實現自己的阻塞佇列(上)佇列
- Java阻塞佇列執行緒集控制的實現Java佇列執行緒
- Java中的阻塞佇列Java佇列
- 阻塞佇列佇列
- 怎樣實現一個非阻塞的超時重試任務佇列佇列
- Java中常用的七個阻塞佇列介紹第一篇Java佇列
- 什麼是阻塞佇列?如何使用阻塞佇列來實現生產者-消費者模型?佇列模型
- Java中常用的七個阻塞佇列第二篇DelayQueue原始碼介紹Java佇列原始碼
- 基於迴圈佇列的BFS的原理及實現佇列
- 訊息佇列的作用以及kafka和activemq的對比佇列KafkaMQ
- Java中幾個常用併發佇列比較 | BaeldungJava佇列
- 死磕阻塞佇列佇列
- 多執行緒程式設計-分析阻塞佇列的原始碼實現執行緒程式設計佇列原始碼
- java併發程式設計工具類JUC第一篇:BlockingQueue阻塞佇列Java程式設計BloC佇列
- 佇列的一種實現:迴圈佇列佇列
- AQS原始碼深入分析之條件佇列-你知道Java中的阻塞佇列是如何實現的嗎?AQS原始碼佇列Java
- 解讀 Java 併發佇列 BlockingQueueJava佇列BloC
- 兩個棧實現佇列佇列
- 幾種排序演算法的原理以及 Java 實現排序演算法Java
- [Java併發程式設計實戰] 阻塞佇列 BlockingQueue(含程式碼,生產者-消費者模型)Java程式設計佇列BloC模型
- 執行緒池的阻塞佇列的理解執行緒佇列
- JAVA中常見的阻塞佇列詳解Java佇列
- 陣列模擬佇列 以及佇列的複用(環形佇列)陣列佇列
- Python佇列的三種佇列實現方法Python佇列