ArrayBlockingQueue 源碼閱讀與分析
通過這個類的名字,可以知道ArrayBlockingQueue是一個底層使用數組實現,具有隊列特點的先進先出以及線程安全的一個集合類,他還可以實現指定時間的阻塞讀寫,也就是可以解決生產者消費者問題的阻塞隊列。
首先來看一下它的構造方法:
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw newIllegalArgumentException();
this.items = newObject[capacity];
lock = newReentrantLock(fair);
notEmpty =lock.newCondition();
notFull = lock.newCondition();
}
這是前兩個構造方法,可以發現一個問題,就是它沒有默認構造器,傳入的參數即爲創建的對象數組的大小,同時初始化鎖和這個鎖上的兩個Condition,一個爲 notEmpty,用作隊列空時,進行take操作的等待;另一個爲notFull,作用是隊列滿時,put操作的等待。
首先來看一下offer方法,offer方法有兩個重載的實現, 用於插入元素至數組的尾部。
先看一下不帶參數的offer方法:
lock.lock();
try {
if (count ==items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
其中的count是隊列中的元素的個數,items就是存值的數組,在數組滿的情況下則不進入等待,而是直接返回 false。不滿的情況下,會進入insert方法,會執行notEmpty.signal();,喚醒再隊列爲空時take操作等待的線程
它還有一個有三個參數的重載方法,如數組已滿,則進入等待,直到以下三種情況時才繼續:被喚醒、到達指定的時間或當前線程被中斷(interrupt)。此方法首先將指定的時間轉換爲納秒,然後執行加鎖操作,如數組未滿,則將對象插入數組,如數組已滿,且已超過指定的時間,則返回 false;如未超過指定的時間,則調用 notFull condition 的 awaitNanos 方法進行等待,如爲被喚醒或超時,則繼續判斷數組是否已滿;如線程被 interrupt,則直接拋出 InterruptedException。
再來看一下put 方法
具體的代碼如下:
public void put(E e) throwsInterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
可以看到,這個方法在數組已滿的情況下會一直等待,直到數組不爲空或線程被 interrupt。
接下來再來看一下poll方法,此方法用於獲取隊列中的第一個元素。和offer一樣,也有兩個重載。
public E poll() {
final ReentrantLocklock = this.lock;
lock.lock();
try {
return (count ==0) ? null : extract();
} finally {
lock.unlock();
}
}
這是不帶時間參數的 poll 方法,它會在在數組中元素個數爲零的情況下則不進入等待,而是直接返回 null,不爲空的情況下執行extract()方法,執行notFull.signal();喚醒put阻塞的線程。
還有一個是有三個參數的poll(E,long,TimeUnit)方法。如隊列中沒有元素,則進入等待,與 offer(E,long,TimeUnit)相同,它也是在三種情況後繼續。 首先將指定的時間轉化爲納秒,並進行加鎖,如數組中的元素個數不爲零,則從當前的對象數組中獲取最後一個元素,在獲取後將該位置上的元素設置爲 null;如數組中的元素個數爲零,首先判斷剩餘的等待時間是否小於零,如小於則返回 null,如大於則調用 notEmpty condition 的 awaitNanos 方法進行等待,如爲被喚醒或超時,則繼續判斷數組中元素個數是否不爲零;如線程被 interrupt,則直接拋出 interruptedException。
最後再看一下take方法,源碼如下:
publicE take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
可以看到,調用 take 方法,此方法在數組爲空的情況下會一直等待,直到數組不爲空或線程被 interrupt。再調用extract方法,取出隊首的元素。