简介
队列(Queue)的实现方式主要有两种,一种是以数组来实现的;另一种是以链表的来实现的。
ArrayBlockingQueue是以固定长度的数组实现的,先进先出;head是队列中存在最久的元素,tail是最新加入的元素,添加元素从队尾添加;删除元素,从队头删除;
不容许插入null; 线程安全,用ReentrantLock实现线程安全;
final Object[] items:元素数组;
int takeIndex:队列队头元素在数组items的下标;
int putIndex:队列队尾元素在数组items的下标+1(即插入当前对象在items中的下标)
int count:队列中元素的个数;
takeIndex和putIndex需要特别注意,ArrayBlockingQueue以固定长度的数组来实现的关键思想;
队列中第X个元素和items数组中存储元素下标,不存在对应关系;有可能队列第X个元素,在items数组中的下标为0,1,2,等情况;
构造函数
构造函数:capacity指定队列固定容量,无法再次更改;默认情况下,默认是不公平的,创建不公平重入锁;
public ArrayBlockingQueue(int capacity) {
this(capacity, 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();
}
插入元素:add(),offer(),put();依次插入到队列;
add():是否插入成功;如果队列容量未满,直接插入;否则报异常队列已满;
public boolean add(E e) {
return super.add(e);
}
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
offer()方法:是否插入成功;先非空检查;插入过程是线程同步的;如果队列已满,返回false;队列未满时,向队列中插入数据,返回true;
public boolean offer(E e) {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
//判断队列count和数组items的length是否相等,如果相等,则队列已满;
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;
//插入之后,putIndex和items数组length相等,则数组尾部已经全部插入元素;将putIndex赋值为0,看看数组头部是否还有空间(因为插入时putIndex从0开始增加;);
if (++putIndex == items.length) putIndex = 0;
count++;
//唤醒一个在await()队列中的线程
notEmpty.signal();
}
put():队列未满,直接插入;队列已满情况下,线程等待;
public void put(E e) throws InterruptedException {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
//当前线程未被中断,则获取锁;如果已经中断则,抛出异常;
lock.lockInterruptibly();
try {
//如果队列已慢,等待;
while (count == items.length)
notFull.await();
//插入数据
enqueue(e);
} finally {
lock.unlock();
}
}
获取元素;element(),peek()
element()方法,主要调用peek()方法
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
peek():根据takeIndex从数组items中取得对应元素;takeIndex默认是0;在删除操作之后,takeIndex+1;
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
}
final E itemAt(int i) {
return (E) items[i];
}
删除元素:poll(),take(),remove()
poll():如果队列count== 0,直接返回null;队列count>0;返回被删除元素;从队列头开始删除元素;
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
dequeue():删除takeIndex对应的元素,
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
//takeIndex
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
//删除之后,takeIndex等于数组length,说明数组items尾部元素,全部删除,看看数组items头部是否还有元素存在;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
//唤醒一个await()队列中的线程;
notFull.signal();
return x;
}
elementDequeued()
void elementDequeued() {
// assert lock.getHoldCount() == 1;
if (count == 0)
queueIsEmpty();
else if (takeIndex == 0)
takeIndexWrapped();
}
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) {
//删除的元素不能为null;应为插入时,非空检查,队列中没有null对象;
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final Object[] items = this.items;
final int putIndex = this.putIndex;
int i = takeIndex;
//从takeIndex到putIndex范围之间循环,如果队列中存在对象o,找到对应的index,删除;不存在直接返回false;
do {
//队列中存在对象o;
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;
//相等话,即队尾删除,和其他两种删除方式一致;直接从数组中删除,takeIndex+1;count-1;
if (removeIndex == takeIndex) {
// 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.
for (int i = removeIndex, putIndex = this.putIndex;;) {
int pred = i;
//如果到数组items尾部,从0开始,继续循环查找;
if (++i == items.length) i = 0;
//直到要移动的下标为putIndex时,将下标pred对应的元素置空,并且将putIndex赋值为pred;退出循环;
if (i == putIndex) {
items[pred] = null;
this.putIndex = pred;
break;
}
//从数组items删除removeIndex对应的元素,然后将从removeIndex+1开始的元素,全部往前移一个位置;
items[pred] = items[i];
}
//队列count减一;
count--;
if (itrs != null)
itrs.removedAt(removeIndex);
}
//唤醒一个await()队列中的线程;
notFull.signal();
}
以上就是ArrayBlockingQueue类的主要方法分析;如有问题,请多指教,谢谢!