作用
有界阻塞队列, 既然是阻塞说明用到了锁,所以本质上各种方法是只能串行,所以该类是线程安全的,比较简单。
实现原理
1、加锁只要是对共享队列的操作,ArrayBlockingQueue使用锁保证线程安全
2、有界数组队列当作,循环队列使用
3、如果队列满了, 入队的线程将会进行阻塞在notFull条件队列上
4、如果队列空了,出队的线程将会阻塞在notEmpty条件队列上
5、添加了元素,将会唤醒notEmpty上的线程
6、元素出队将会唤醒notFull上的线程。
字段属性
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/**
* Serialization ID. This class relies on default serialization
* even for the items array, which is default-serialized, even if
* it is empty. Otherwise it could not be declared final, which is
* necessary here.
*/
private static final long serialVersionUID = -817911632652898426L;
/** 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; //元素个数
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
/** Main lock guarding all access */
final ReentrantLock lock; //锁
/** Condition for waiting takes */
private final Condition notEmpty; //空了
/** Condition for waiting 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.
*/
transient Itrs itrs = null; //迭代器
}
构造方法
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();
}
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair); //队列元素进行初始化
final ReentrantLock lock = this.lock; //final 保证了可见性,而且不能修改引用
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;//注意 队列是逻辑头尾环形队列处理 putIdex指下一个入队的位置
} finally {
lock.unlock();
}
}
offer
public boolean offer(E e) { //从队尾添加一个
checkNotNull(e); //检查不等于空
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length) //说明队列满了
return false;
else {
enqueue(e); //入队
return true;
}
} finally {
lock.unlock();
}
}
enqueue
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(); //唤醒一个等待队列里面的线程
}
poll
public E poll() {
final ReentrantLock lock = this.lock; //获取锁
lock.lock(); //加锁
try {
return (count == 0) ? null : dequeue(); //是否有元素可以出队
} finally {
lock.unlock(); //解锁
}
}
dequeue
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;
}
take
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //阻塞的时候可以响应中断
try {
while (count == 0) //没有出队的
notEmpty.await(); //阻塞
return dequeue(); //出队
} finally {
lock.unlock();
}
}
remove
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();
}
}
removeAt
void removeAt(final int removeIndex) {
// assert lock.getHoldCount() == 1;
// assert items[removeIndex] != null;
// assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
if (removeIndex == takeIndex) { //说明是刚刚好在出队位置, 直接当作一次peek就行
// removing front item; just advance
items[takeIndex] = null; //置空
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else { //不是出的位置, 需要后面的元素往前移动
// an "interior" remove
// slide over all others up through putIndex.
final int putIndex = this.putIndex; //入队位置
for (int i = removeIndex;;) { //当前位置开始
int next = i + 1;
if (next == items.length)
next = 0;
if (next != putIndex) { //不到末尾进行覆盖
items[i] = items[next];
i = next;
} else { //到了末尾进行置空
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
if (itrs != null)
itrs.removedAt(removeIndex);
}
notFull.signal(); //唤醒
}
迭代器功能
直接翻译注释了, 迭代器整体的思想还是挺难的。
迭代器及其队列之间的共享数据,允许队列修改在删除元素时更新迭代器。为了正确处理一些不常见的操作,这增加了很多复杂性,但圆形数组和支持内部删除(即那些不在头部的)的组合会导致迭代器有时丢失它们的位置和/或(重新)报告它们不应该报告的元素。为了避免这种情况,当一个队列有一个或多个迭代器时,它通过以下方式保持迭代器状态的一致性:(1)跟踪“循环”的数量,即takeIndex被包装到0的次数。(2)当一个内部元素被移除时,通过回调函数removedAt通知所有的迭代器(因此其他元素可能会被移动)。这些足以消除迭代器的不一致性,但不幸的是增加了维护迭代器列表的次要责任。我们跟踪一个简单链表(仅在队列锁被持有时访问)中对Itr的弱引用的所有活动迭代器。使用3种不同的机制来清理列表:(1)每当创建新的迭代器时,执行O(1)检查过时的列表元素。(2)当takeIndex绕到0时,检查是否有超过一个循环周期未使用的迭代器。(3)当队列变为空的时候,所有的迭代器都被通知,整个数据结构被丢弃。因此,除了确保正确性所必需的removedAt回调外,迭代器还有shutdown和takeIndexWrapped回调,可以帮助从列表中删除过时的迭代器。每当检查一个列表元素时,如果GC确定迭代器被丢弃,或者迭代器报告它已“分离”(不需要任何进一步的状态更新),它就会被删除。当takeIndex从不前进,迭代器在耗尽之前被丢弃,并且所有删除都是内部删除时,开销是最大的,在这种情况下,所有过时的迭代器都会被GC发现。但即使在这种情况下我们也不会增加平摊复杂度。必须注意防止列表清除方法重入调用另一个这样的方法,从而导致微妙的损坏bug。
多读几遍多想。
迭代器表???
class Itrs {
/**
* Node in a linked list of weak iterator references.
*/
private class Node extends WeakReference<Itr> { //弱引用队列, 即迭代器表的节点
Node next; //指向下一个迭代器节点
Node(Itr iterator, Node next) {
super(iterator); //当前Node节点的迭代器
this.next = next; //指向下一个迭代器
}
}
/** Incremented whenever takeIndex wraps around to 0 */
int cycles = 0; //takeIndex为0 , cycles进行加一
/** Linked list of weak iterator references */
private Node head; //指向迭代器链表的头指针
/** Used to expunge stale iterators */
private Node sweeper = null; //下次开始进行遍历进行迭代器清理的位置, 这里是为了避免进行全局遍历的优化,比如有1000个迭代器,如果遍历了20个迭代器还是没有发现已被GC回收的线程,那么就结束本次遍历,如果发现一个那么遍历次数为0,继续20个接着遍历
private static final int SHORT_SWEEP_PROBES = 4; //长度为4的遍历长度
private static final int LONG_SWEEP_PROBES = 16; //长度为16的遍历长度
}
这里面的知识点除了弱引用, 需要考虑清楚cycles和sweeper的作用是为了什么,对于cycles我的理解就是记录当前队列出队从新经过对头的次数, 应为迭代器是共享的同一个队列,如果别的线程一直在poll, 这样会导致元素原本的一轮上的节点全不被出队掉了, 所以迭代器也就没有遍历的必要
Itrs构造方法
Itrs(Itr initial) {
register(initial); //进行注册迭代器到链表中, 即头节点的初始化
}
void register(Itr itr) {
// assert lock.getHoldCount() == 1;
head = new Node(itr, head); //头插入法
}
迭代器对象
还是直接翻译注释吧, 大佬设计思想:
迭代器之间。为了保持放取操作的弱一致性,我们提前读取一个槽位,这样就不会报告hasNext为真,但也没有要返回的元素。当所有索引都为负数,或者hasNext第一次返回false时,我们切换到“分离”模式(允许在没有GC帮助的情况下从itrs提示断开链接)。这允许迭代器完全准确地跟踪并发更新,除了用户在hasNext()返回false后调用iterator .remove()的特殊情况。即使在这种情况下,我们也可以通过跟踪lasttitem中要删除的元素来确保不会删除错误的元素。是的,我们可能无法从队列中移除lasttem,如果它在分离模式下因为交错的内部移除而移动。
数据结构
private class Itr implements Iterator<E> {
/** Index to look for new nextItem; NONE at end */
private int cursor; //指向新的nextItem
/** Element to be returned by next call to next(); null if none */
private E nextItem; //避免内部节点删除,迭代器hashNext的下一个节点
/** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
private int nextIndex; //下一个节点出队位置
/** Last element returned; null if none or not detached. */
private E lastItem; // 最后遍历到的元素
/** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
private int lastRet; //追后遍历到的索引下标
/** Previous value of takeIndex, or DETACHED when detached */
private int prevTakeIndex; //上一次进行take的位置记录
/** Previous value of iters.cycles */
private int prevCycles;//记录循环从头开始遍历的次数
/** Special index value indicating "not available" or "undefined" */
private static final int NONE = -1; //没有值
/**
* Special index value indicating "removed elsewhere", that is,
* removed by some operation other than a call to this.remove().
*/
private static final int REMOVED = -2; //被移除
/** Special value for prevTakeIndex indicating "detached mode" */
private static final int DETACHED = -3; //被分离
迭代器构造方法
Itr() {
// assert lock.getHoldCount() == 0;
lastRet = NONE; //最后遍历的节点, 初始为空
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock(); //加锁
try {
if (count == 0) { //队列元素为空, 迭代器没有必要了
// assert itrs == null;
cursor = NONE; //空
nextIndex = NONE; //空
prevTakeIndex = DETACHED; //分离
} else {
final int takeIndex = ArrayBlockingQueue.this.takeIndex; //获取出队位置
prevTakeIndex = takeIndex;
nextItem = itemAt(nextIndex = takeIndex); //获取下一个出队的元素
cursor = incCursor(takeIndex); //指向最新的nextItem索引
if (itrs == null) { //迭代器表
itrs = new Itrs(this); //初始化迭代器表
} else {
itrs.register(this); // in this order 当前迭代器进行注册到迭代器表
itrs.doSomeSweeping(false); //进行一次尝试迭代器清理
}
prevCycles = itrs.cycles; //当前队列的次数保持一直
// assert takeIndex >= 0;
// assert prevTakeIndex == takeIndex;
// assert nextIndex >= 0;
// assert nextItem != null;
}
} finally { //释放锁
lock.unlock();
}
}
迭代器的初始化的时候进行了一个迭代器表的初始化
doSomeSweeping
扫描 itrs,寻找并删除过时的迭代器。如果至少找到一个,请努力寻找更多。仅从迭代线程调用。
void doSomeSweeping(boolean tryHarder) { //进行遍历删除被垃圾回收的迭代器节点进行脱离链表
// assert lock.getHoldCount() == 1;
// assert head != null;
int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES; //探针 尝试要找几个
Node o, p;
final Node sweeper = this.sweeper; //清扫到哪里了
boolean passedGo; // to limit search to one full sweep
if (sweeper == null) {//没有标记从头开始清理
o = null;
p = head;
passedGo = true; //全局清理
} else {
o = sweeper; //从上一次清理的地方开始
p = o.next; //接下来要清理的位置
passedGo = false; //不是全局清理
}
for (; probes > 0; probes--) { //步长内进行遍历
if (p == null) {//说明到了迭代器链表末尾
if (passedGo) //说明是从头开始的遍历到末尾
break;
o = null;//这里准备重新从头到尾巴进行遍历一次
p = head;
passedGo = true;
}
final Itr it = p.get(); //获取迭代器
final Node next = p.next; //获取p的后继
if (it == null || it.isDetached()) { //如果改迭代器已经被垃圾回收 或者 是被分离了
// found a discarded/exhausted iterator
probes = LONG_SWEEP_PROBES; // "try harder" 既然有一个进行了清理, 说明更多的,所以探针范围扩大
// unlink p
p.clear();//清楚p的迭代器
p.next = null; //断开链表后继
if (o == null) { //说明是从迭代器链表头部开始遍历
head = next; //头指向后继
if (next == null) {//到底了 , 清理完毕
// We've run out of iterators to track; retire
itrs = null; //迭代器表已经不需要了,已经迭代器表里已经没有迭代器了
return;
}
}
else
o.next = next; //指向下一个, 跨过找个过时的迭代器
} else {
o = p; //继续往后, 没有过时的迭代器, o 往后走
}
p = next; //p往后走
}
this.sweeper = (p == null) ? null : o; //记录扫描位置
hasNext
出于性能原因,我们不希望在常见情况下获取 hasNext 中的锁。为此,我们只访问未被队列修改触发的更新操作修改的字段(即 nextItem)
public boolean hasNext() {
// assert lock.getHoldCount() == 0;
if (nextItem != null) //下一项不为空
return true;
noNext(); //说明迭代器已经到底了, 需要进行分离
return false;
}
next
public E next() {
// assert lock.getHoldCount() == 0;
final E x = nextItem; //获取一个出队的元素
if (x == null)
throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock(); //加锁
try {
if (!isDetached()) //当前迭代器没有被分离
incorporateDequeues(); //合并出队
// assert nextIndex != NONE;
// assert lastItem == null;
lastRet = nextIndex; //lastRet 下标进行替换
final int cursor = this.cursor;//
if (cursor >= 0) { //还有最新下标元素可以进行遍历
nextItem = itemAt(nextIndex = cursor);
// assert nextItem != null;
this.cursor = incCursor(cursor); //获取最新的nextItem下标
} else {//说明迭代器已经遍历到头了
nextIndex = NONE;
nextItem = null;
}
} finally {
lock.unlock();
}
return x;
}
elementDequeued
每当元素出队时调用(在 takeIndex 处)。
void elementDequeued() {
// assert lock.getHoldCount() == 1;
if (count == 0) //队列元素为0
queueIsEmpty(); //清空迭代器表
else if (takeIndex == 0) //出队循环到从头开始
takeIndexWrapped(); //移除没有跟上的迭代器迭代器
}
queueIsEmpty
每当队列变空时调用。通知所有活动的迭代器队列为空,清除所有弱引用,并取消链接 itrs 数据结构。
void queueIsEmpty() {
// assert lock.getHoldCount() == 1;
for (Node p = head; p != null; p = p.next) { //从头到尾进行链表遍历
Itr it = p.get();
if (it != null) {
p.clear(); //清楚
it.shutdown(); //关闭
}
}
head = null; //清除指向
itrs = null; //清除
}
shutdown
调用来通知迭代器队列是空的,或者它已经无可救药地落后了,因此它应该放弃任何进一步的迭代,除了可能从 next() 返回一个元素,正如从 hasNext() 返回 true 所承诺的那样。
void shutdown() {
// assert lock.getHoldCount() == 1;
cursor = NONE; //最新的nextItem为空
if (nextIndex >= 0)
nextIndex = REMOVED; //移除
if (lastRet >= 0) {
lastRet = REMOVED; //移除
lastItem = null;
}
prevTakeIndex = DETACHED; //分离
// Don't set nextItem to null because we must continue to be
// able to return it on next().
//
// Caller will unlink from itrs when convenient.
}
不要将 nextItem 设置为 null,因为我们必须继续能够在 next() 上返回它。来电者将在方便时取消与 itrs 的链接。
takeIndexWrapped
每当 takeIndex 回绕到 0 时调用。通知所有迭代器,并删除任何现在过时的迭代器。
void takeIndexWrapped() { //当takeIndex为0, 进行迭代器清理工作
// assert lock.getHoldCount() == 1;
cycles++; //次数加一
for (Node o = null, p = head; p != null;) {//o 记录为p的头指针, 开始进行遍历
final Itr it = p.get(); //获取迭代器
final Node next = p.next; //获取p节点的后继
if (it == null || it.takeIndexWrapped()) { //当前迭代器被GC回收了或者当前迭代器是否是需要被回收(即跟步上)
// unlink p
// assert it == null || it.isDetached();
p.clear(); //置空
p.next = null; //从迭代器链表进行脱离
if (o == null) //说明这个迭代器节点是头节点
head = next; //头指针指向next
else
o.next = next; //跨过p节点
} else { //p不需要进行清理
o = p; //o指向p
}
p = next; //推进
}
if (head == null) // no more iterators to track 判断迭代器表里已经没有迭代器了
itrs = null; //进行空间回收
}
takeIndexWrapped
boolean takeIndexWrapped() { //判断当前迭代器是否是需要被回收
// assert lock.getHoldCount() == 1;
if (isDetached()) //如果迭代器被分离了
return true;
if (itrs.cycles - prevCycles > 1) { //当前迭代器是否没有被甩了一圈, 哈哈哈,这个形容词比较好。
// All the elements that existed at the time of the last
// operation are gone, so abandon further iteration. 次操作时存在的所有元素都消失了,因此放弃进一步的迭代。
shutdown(); //迭代器进行关闭
return true;
}
return false;
}
removedAt (itrs)
每当发生内部删除(不在 takeIndex 处)时调用。
void removedAt(int removedIndex) { //移除节点通知迭代器(不是栈顶的时候的移除才会进行调用)
for (Node o = null, p = head; p != null;) {//开始遍历迭代器链表
final Itr it = p.get(); //获取迭代器
final Node next = p.next; //获取迭代器的下一个
if (it == null || it.removedAt(removedIndex)) { //如果迭代器为空或者迭代器分离了
// unlink p
// assert it == null || it.isDetached();
p.clear(); //清楚
p.next = null;
if (o == null)
head = next;
else
o.next = next;
} else {
o = p;
}
p = next;
}
if (head == null) // no more iterators to track
itrs = null;
}