ArrayBlockingQueue为BlockingQueue的实现类,常用的阻塞队列,先来看一下次类的成员变量:
/** 存放数据的数组*/
final Object[] items;
/**队列出口数据在数组中的下标*/
int takeIndex;
/**队列入口在数组中的下标*/
int putIndex;
/**存放数据数组的实际长度(被存放了数据的长度)*/
int count;
/**并发用到的locak*/
final ReentrantLock lock;
/**可以从队列里取出数据的条件变量*/
private final Condition notEmpty;
/**向队列输入数据的条件变量*/
private final Condition notFull;
根据上面代码中的注释,大家应该知道成员变量的作用
下面我们来看几个常用的方法的源码:
1 构造方法:
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();
}
}
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对象时,可以指定阻塞队列的长度,Lock的类型(公平锁或是非公平锁),以及向队列初始化一些数据,加入数据的时候要加锁!在finally语句块里释放锁
2 向队列添加元素 add offer put
public boolean add(E e) {
return super.add(e);
}
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
add 方法的具体实现在父类中:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
可以看到add的具体实现调用的是offer方法,如果队列已满,则会抛出IllegalStateException异常
在offer方法中,如果队列已满,则返回false(添加过程使用Lock,保证线程安全)
在put方法中,如果队列已满,条件变量notFull调用await方法,使其他向队列插入数据的线程阻塞
3 从队列取出元素 take poll peek
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return extract();
} finally {
lock.unlock();
}
}
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : itemAt(takeIndex);
} finally {
lock.unlock();
}
}
take 从阻塞队列里去取数据,整个过程加入中断锁,当队列中的数据为空时,notEmpty条件变量阻塞队列上的取数据的线程
poll 整个取数据的过程加锁, 支持设置等待时间
peek 此方法并不是取数据,而是读取数据,把队列出口处的数据返回,队列长度并没有减小,整个过程中加锁!
把ArrayBlockingQueue源码拿出来分析一下的原因是:阻塞队列实现线程阻塞和安全是由Condition和Lock一起来实现的,所以要深入掌握Lock和Condition的使用!
本文详细解读ArrayBlockingQueue类的构造方法、常用方法源码,包括如何通过Condition和Lock实现线程阻塞和安全,以及如何在创建对象时指定阻塞队列长度、锁类型和初始化数据。同时,介绍了add、offer、put、take、poll和peek等方法的具体实现,特别关注了队列满和空时的处理机制。

被折叠的 条评论
为什么被折叠?



