整体流程
组成元素
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();
}
}