ArrayBlockingQueue
是一个有界的队列,遵循先进先出规则,头部存放的永远是该队列中存储时间最长的元素,尾部是存储最短时间的元素。
新元素从队列尾部进入,从头部取出元素。
该队列一旦被创建,就无法改变其容量。
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and default access policy.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
创建时给定容量。
BlockingQueue<User> queue = new ArrayBlockingQueue<>(10,true);
for (int i = 0; i < 11; i++) {
try {
queue.put(new User(i));
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程未被阻塞");
运行结果:线程未被阻塞
字样未被打印。
当尝试向已满队列中添加元素时线程会被阻塞,当尝试从空队列中取出元素时线程同样也会被阻塞。
//源码
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //禁止其他线程进入
try {
while (count == items.length) //自旋查看是否有空位
notFull.await(); //原子级别释放锁
enqueue(e);
} finally {
lock.unlock();
}
}
调用者进入此方法,获得锁后进入自旋。count == items.length
判断队列是否已满,如果条件为真则暂时放弃该锁,将执行权让给其他线程。
若此时队列已满,则所有生产者线程都将被阻塞(也有可能生产者线程被阻塞部分,因为消费者被率先执行了,生产则并没有争取到执行机会),此时锁被其他消费者中的一个所持有,进行消费直到有空位。
此时消费者在队列已满时争取到了机会执行take()
方法。
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock; //消费者之一获取到锁
lock.lockInterruptibly();
try {
while (count == 0) //由于队列已满,count = items.length 则该表达式 = false
notEmpty.await(); //不会执行此句
return dequeue(); //调用dequeue出队方法
} finally {
lock.unlock();
}
}
其中一个消费者率先抢到机会执行take()
方法,首先获得锁,避免过多消费者同时消费。
自旋判断是非队列为空,由于我们假设队列已满,则该条件为false
,不会执行notEmpty.await()
阻塞队列。
调用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;
}
该队列默认使用非公平锁,也可以通过调用构造器时传入一个Boolean
值来选择使用公平或非公平锁。
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();
}
-
公平锁
即当并发访问该队列时,无论是对其插入或删除。线程的执行顺序应该是线程的请求顺序。遵守先进先出规则,先请求的先执行,类似于排队。
-
非公平锁
即当并发访问该队列时,无论是对其插入或删除。线程的执行顺序是随机的,谁抢得到执行权谁执行。这种方式往往效率更高,所以
ArrayBlockingQueue
默认采用非公平锁。