BlockingQueue使用方式 及 源码阅读
心血来潮,想了解一下BlockingQueue
的实现原理。这里以ArrayBlockingQueue
为例子分两部分进行了解:
- 使用方式
- 源码阅读
ArrayBlockingQueue
的源码实现中用到了ReentrantLock
(重入锁),对其使用方式不太了解的同学可以查看:
ReentrantLock(重入锁)使用方式
使用方式
BlockingQueue queue = new ArrayBlockingQueue(3);
// 向队列中插入数据
queue.put(1);
// 从队列中取出数据
queue.take();
源码阅读
注:源码来自android-25
先看ArrayBlockingQueue
构造方法:
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();
}
向ArrayBlockingQueue
中插入数据:
public void put(E e) throws InterruptedException {
// 判空
Objects.requireNonNull(e);
// 重入锁对象
final ReentrantLock lock = this.lock;
// 获取可中断锁
lock.lockInterruptibly();
//
try {
// 如果队列已满,阻塞当前线程,等待队列释放出空间后的 notFull.signal() 信号
while (count == items.length){
notFull.await();
}
// 如果队列未满,则将数据插入队列中
enqueue(e);
} finally {
// 释放锁
lock.unlock();
}
}
// 向队列中插入数据
private void enqueue(E x) {
// 数组构造的队列
final Object[] items = this.items;
// 数据添加到队列末尾
items[putIndex] = x;
// putIndex +1 与数组长度相同
if (++putIndex == items.length) {
// index 归零,下次从index为0插入数据
putIndex = 0;
}
// 队列中item数量
count++;
// 发送数组不为空的信号,唤醒对应的线程
notEmpty.signal();
}
- 向队列中插入数据时,如果队列已经满了,则调用
notFull.await()
让线程进入等待状态;直到队列中取出数据notFull.signal()
信号发出后,才能重新唤醒添加数据的线程。 - 向队列中插入数据时,不断向后边插入数据。如果当前插入的数据,是数组的最后一条数据,(只要数组不满)则下次从index为0的数组位置继续依次插入数据,从而形成一个循环。
ArrayBlockingQueue
中出队列:
public E take() throws InterruptedException {
// 重入锁
final ReentrantLock lock = this.lock;
// 获取可中断锁
lock.lockInterruptibly();
//
try {
// 如果数组中没有数据,阻塞当前线程,进入等待状态,等待notEmpty.signal()信号
while (count == 0){
notEmpty.await();
}
// 如果数组中有数据 出队列
return dequeue();
} finally {
// 释放锁
lock.unlock();
}
}
// 从队列中取出数据
private E dequeue() {
// 数组
final Object[] items = this.items;
// 顺序取出数据
E x = (E) items[takeIndex];
// 数组置空
items[takeIndex] = null;
// 如果取出的数据是 数组的最后一条 下次从头开始取数据
if (++takeIndex == items.length) {
takeIndex = 0;
}
// 队列中数据的数量减少一个
count--;
// 忽略...
if (itrs != null){
itrs.elementDequeued();
}
// 发送 数据不为空 的信号
notFull.signal();
return x;
}
- 出队列时,如果队列已经没有数据了,则调用
notEmpty.await()
让线程进入等待状态;直到线程中加入数据notEmpty.signal()
信号发出后,才能唤醒取数据的线程。 - 出队列时,不断从前向后取。如果当前取出的数据,是数组的最后一条数据,(只要数组不空)则下次从index为0的数组位置继续依次取数据,从而形成一个循环。