学习ArrayBlockingQueue需要掌握ReentrantLock 原理,或者了解其使用也行。
java 并发编程系列文章目录
按照类注释写的:ArrayBlockingQueue是一个 “由数组支持的有界阻塞队列”,该队列对元素FIFO(先进先出)进行排序。队列的头是在队列中停留时间最长的元素。队列的尾部是在队列中停留时间最短的元素。新元素被插入到队列的尾部,队列检索操作获得队列头部的元素。
通过核心属性就可以看出,通过ReentrantLock 实现线程安全的对数组items进行操作
//数据存储的数组
final Object[] items;
//把元素取出的下标index
int takeIndex;
//把元素放入的下标index
int putIndex;
//数组中元素的个数统计字段
int count;
//保证原子性的可重入锁
final ReentrantLock lock;
//两个等待条件
private final Condition notEmpty;
private final Condition notFull;
public boolean add(E e) {
return super.add(e);
}
public boolean add(E e) {
if (offer(e))
return true;
else
//如果offer失败,返回的fasle add方法是抛出异常的
throw new IllegalStateException("Queue full");
}
offer方法会有个boolean值返回,代表添加成功或者失败,但是add方法,如果添加失败会抛出异常
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
// 上面已经说了,ArrayBlockingQueue是有界阻塞队列,如果当前items的元素数量count已经等于数组的长度了,则添加失败
if (count == items.length)
return false;
else {
//
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
final Object[] items = this.items;
//数组中放入元素
items[putIndex] = x;
//防止数组下标越界
if (++putIndex == items.length)
putIndex = 0;
//数组元素的数量+1
count++;
//唤醒等待获取元素的线程从数组中获取数据
notEmpty.signal();
}
put方法也是添加一个元素,区别offer在于如果当前items数组已经放满了,他会阻塞等待,等待别的线程从数组中把元素取走,然后唤醒它去添加元素。
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
//items数组已经放满 会阻塞在这等待
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
获取一个元素,如果没有元素会阻塞等待直到有元素时获取并返回
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//加锁保证取元素的原子性
lock.lockInterruptibly();
try {
while (count == 0)
//当items数组为空时,会阻塞等待直到items数组中被放入元素,会notEmpty.signal()
notEmpty.await();
//从items获取一个元素返回
return dequeue();
} finally {
lock.unlock();
}
}
//从items 获取一个元素
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
//获取元素
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
//维护takeindex
takeIndex = 0;
//元素数量-1
count--;
if (itrs != null)
itrs.elementDequeued();
//唤醒如put方法添加数据但元素已满阻塞在那的线程进行添加元素
notFull.signal();
return x;
}
和take方法相比会返回null 而不是阻塞
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//如果没有元素则返回null 不是阻塞
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//返回数组中的元素,和dequeue 相比 他没有把数组中的元素设置成null 只是拿到这个元素
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
}
final E itemAt(int i) {
return (E) items[i];
}
ArrayBlockingQueue 的size方法也是强一致性的
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//返回当前items中元素的数量
return count;
} finally {
lock.unlock();
}
}
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) {
//移除当前元素,并唤醒添加元素阻塞的线程
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);//就是遍历所有的元素进行比较
}
return false;
} finally {
lock.unlock();
}
}
和remove方法一样,只不过找到元素,会返回true, remove会移除元素
public boolean contains(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i]))
return true;
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
把多少个元素转移到指定的c集合中去。
public int drainTo(Collection<? super E> c, int maxElements) {
checkNotNull(c);
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
//取最小值,可能当前集合中都没有这么多元素
int n = Math.min(maxElements, count);
int take = takeIndex;
int i = 0;
try {
while (i < n) {
//元素取出 添加到c集合中,自身items元素移除
@SuppressWarnings("unchecked")
E x = (E) items[take];
c.add(x);
items[take] = null;
if (++take == items.length)
take = 0;
i++;
}
return n;
} finally {
//... notFull.signal();
}
} finally {
lock.unlock();
}
}