ArrayBlockingQueue原理
ArrayBlockingQueue有一个定长数组用来存储存放的元素,并用一个属性count记
录存储元素的个数,每当调用take这样的方法是,ArrayBlockingQueue会根据自
己当前记录的takeIndex索引值,来获取对应索引处的元素,每当取到元素则
takeIndex++,计算下次take的索引位置,知道takeIndex = 数组长度,则将
takeIndex置为0.ArrayBlockingQueue的put方法也类似也有一个putIndex索引,从
索引0处开始放,方法putIndex等于数组长度时,则置为0.至于如何保证同步就是
使用ReentrantLock锁,与2个Condition来调度put 与take
API
public ArrayBlockingQueue(int capacity)
初始化ArrayBlockingQueue ---存储最大数目为capacity
public ArrayBlockingQueue(int capacity, boolean fair)
初始化ArrayBlockingQueue ---存储最大数目为capacity 公平/非公平可选
public boolean add(E e)
添加一个元素到ArrayBlockingQueue,添加失败抛异常
public void clear()
清空ArrayBlockingQueue中的元素
public boolean offer(E e)
添加一个元素到ArrayBlockingQueue
public E peek()
获取当前takeIndex处的元素
public E poll()
获取当前takeIndex处的元素,并删除该元素
public void put(E e) throws InterruptedException
阻塞的获取当前takeIndex处的元素,并删除该元素
public E take() throws InterruptedException
阻塞的添加一个元素到ArrayBlockingQueue,
源码分析
初始化
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
存储数组
this.items = new Object[capacity];
同步锁
lock = new ReentrantLock(fair);
调度Condition
notEmpty = lock.newCondition();
调度Condition
notFull = lock.newCondition();
}
成员属性
/** The queued items */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
/** Number of elements in the queue */
int count;
add方法
public boolean add(E e) {
调用父类实现
return super.add(e);
}
父类实现
public boolean add(E e) {
调用子类offer方法
if (offer(e))
return true;
else //添加失败则直接异常
throw new IllegalStateException("Queue full");
}
offer方法
public boolean offer(E e) {
/** 非空检查
* private static void checkNotNull(Object v) {
* if (v == null)
* throw new NullPointerException();
* }
*/
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
private void insert(E x) {
putIndex处插入元素
items[putIndex] = x;
/**
putIndex索引插入到数组尾部,重置索引为0
final int inc(int i) {
return (++i == items.length) ? 0 : i;
}
*/
putIndex = inc(putIndex);
记录数组中元素个数
++count;
插入成功唤醒,获取元素take方法停止的线程
notEmpty.signal();
}
poll方法
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : extract();
} finally {
lock.unlock();
}
}
private E extract() {
final Object[] items = this.items;
获取takeIndex处的元素
E x = this.<E>cast(items[takeIndex]);
清空takeIndex索引处存储的引用
items[takeIndex] = null;
/**
takeIndex索引插入到数组尾部,重置索引为0
final int inc(int i) {
return (++i == items.length) ? 0 : i;
}
*/
takeIndex = inc(takeIndex);
记录数组中元素个数
--count;
唤醒put方法对应的插入线程
notFull.signal();
return x;
}
put方法
put方法等待在notFull这个Condition上
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
take方法
take等待在notEmpty这个Condition
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
总结
1.ArrayBlockingQueue不可以插入null
2.只能存储固定数目的数据
3.java8以后ArrayBlockingQueue引入了若引用WeakReference,能力有限有待研究