整體流程
組成元素
1)ReentrantLock:重入鎖,對操作加鎖,實現併發訪問控制,以及內存可見性
2)ReentrantLock.newCondition(notFull):非滿條件,數據出隊後,發送notFull信號(signal),通知因隊滿等待的線程,喚醒搶鎖。
3)ReentrantLock.newCondition(notEmpty):非空條件,數據入隊後,發送notEmpty信號,通知因隊空等待的線程,喚醒搶鎖。
4)items數組等基本隊列元素
流程描述
以插入爲例:
- 獲取鎖
- 循環判斷當前元素個數是否等於數組長度(有界隊列)
- 不等於,說明隊列未滿,直接退出循環,或者notFull.await(),釋放鎖,進入並掛起,代碼進入自旋狀態,等待notFull signal信號。否則進入5
- 等到notFull信號後,如果獲得鎖,則繼續4
- 等於則直接入隊,併發起notEmpty信號,喚醒等待此信號的線程。
- 釋放鎖
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();
}
}
/**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
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();
}
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
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(); // 出隊後發送notFull信號
return x;
}
一些特殊點
1)構造函數加lock, 由於putIndex 、count等元素都爲使用volatile關鍵字修飾,所以如果不加lock,修改後的值不會立即同步到主存,所以需要lock獲得內存可見性,刷進主存:
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();
}
}