BlockingQueue
介绍
BlockingQueue即阻塞队列,它是基于ReentrantLock,依据它的基本原理,我们可以实现Web中的长连接聊天功能,当然其最常用的还是用于实现生产者与消费者模式,大致如下图所示:
在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。
阻塞与非阻塞
入队
add(E e) :将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false–>不阻塞
put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断–>阻塞、
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:–>阻塞
被唤醒
等待时间超时
当前线程被中断
出队
poll():如果没有元素,直接返回null;如果有元素,出队
take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断–>阻塞
remove(Object o) :从此队列中移除指定元素的单个实例(如果存在)
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
被唤醒
等待时间超时
当前线程被中断
Java线程池中常用的BlockingQueue的实现
ArrayBlockingQueue源码分析
主要乘员变量介绍
/** 队列存储的地方 */
final Object[] items;
/** 当前队列下一个take, poll, peek or remove的下标 */
int takeIndex;
/** 队列put,offer或add的下标 */
int putIndex;
/**队列现有总数量*/
int count;
/** 所有入口主锁 */
final ReentrantLock lock;
/** takes的等待条件 */
private final Condition notEmpty;
/** puts的等待条件 */
private final Condition notFull;
/**
* Shared state for currently active iterators, or null if there
* are known not to be any. Allows queue operations to update
* iterator state.
* 当前活动迭代器的共享状态;如果存在,则为null 众所周知不是。 允许队列操作更新
迭代器状态。
*/
transient Itrs itrs = null;
构造函数介绍
/**固定长度队列,非公平锁*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* 固定长度队列,fair:ture公平锁,fair:false非公平锁
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
/**
* 固定长度,fair:ture公平锁,fair:false非公平锁,可加入,小于等于capacity的集合
*/
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();
}
}
通过 putIndex = (i == capacity) ? 0 : i;可以看出,当队列i==capacity时即队列添加到数组的最后一位的时候,将新增索引置为0,(当索引为0的值取出时,就可以根据putIndex继续添加了)
put和take主要函数介绍
/**
* 如果队列满了,notFull.await()一直阻塞,直到队列不满了或者线程被中断-->阻塞
*/
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();
}
}
//如果队列空了,一直阻塞,直到队列不为空或者线程被中断-->阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
/**
* 向队列中插入数据,能够执行到该步骤说明,count<this.items.length
* 当下一个putIndex == items.length时,将数组位置指向0继续插入,插入完成
* notEmpty.signal()唤醒正在等待take的操作去去队列中的值
*/
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();
}
/**
* 从队列中取数据,能够执行到该步骤说明,count>0
* 当下一个takeIndex == items.length时,将数组位置指向0继续取,取出后有空位
* notFull.signal()唤醒正在等待put的操作向队列中存值
*/
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;
}