前言
ArrayBlockingQueue這個是數組阻塞隊列,是BlockingQueue接口的一個實現類,它是一個容量有限的隊列,一旦隊列大小確認了就不能再改變了,使用方法,下面介紹。
使用
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Main1 {
public static void main(String[] args) {
// 準備好數組阻塞隊列
BlockingQueue<Integer> bq = new ArrayBlockingQueue<Integer>(1024);
// 兩個Runnable實現類
Producer producer = new Producer(bq);
Consumer consumer = new Consumer(bq);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
// 線程開啓
t1.start();
t2.start();
}
}
class Producer implements Runnable {
private BlockingQueue<Integer> blockingQueue = null;
public Producer() {
}
public Producer(BlockingQueue<Integer> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
// try catch主要是put(null),會拋出Exception
try {
// 爲了實現一個生產慢,消費快,消費阻塞的效果
Thread.sleep(1000);
blockingQueue.put(1);
Thread.sleep(1000);
blockingQueue.put(4);
Thread.sleep(1000);
blockingQueue.put(555);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private BlockingQueue<Integer> blockingQueue = null;
public Consumer() {
}
public Consumer(BlockingQueue<Integer> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
源碼解析
其實這個ArrayBlockingQueue的源碼並不難,底層就是一個數組在進行操作,我們通過上面的那個Demo代碼,進行debug,然後來探究一下到底底層是如何操作的。
// 實例化就不多說了
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and default access policy.
*
* @param capacity the capacity of this queue 隊列的容量
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
// 判斷傳進來設置的隊列容量是不是正整數
if (capacity <= 0)
// 不是肯定要拋出異常,你玩兒我?
throw new IllegalArgumentException();
// final Object[] items; 這個items就是個Object類型的數組
this.items = new Object[capacity];
// 這個鎖肯定是要用的,用的是ReentrantLock
lock = new ReentrantLock(fair);
// 兩個監視器用於隊列滿和隊列用阻塞之時使用。
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
// 當生產者調用put方法時
public void put(E e) throws InterruptedException {
// 查看放進隊列的對象是不是null,是則拋出NPE
checkNotNull(e);
// 拿到前面初始化時候的那把鎖
final ReentrantLock lock = this.lock;
// 加鎖,其他線程沒法用下面的方法了
lock.lockInterruptibly();
try {
// 看看這個隊列裏面有多少個數,是不是比容量大了
while (count == items.length)
// 隊列滿了,就等待
notFull.await();
// 沒滿就入列
enqueue(e);
} finally {
// 解鎖操作不能忘
lock.unlock();
}
}
// 入列操作
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();
}
// 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() {
// 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;
}
後記
所以,其實我們後面大部分業務場景用的就是用這個數組阻塞隊列,不過,既然是學習的話,當然是其他實現類也是要看看了。