基于jdk1.8
数组阻塞队列 线程安全的
底层:对象数组+ReentrantLock + notFullCondition + notFullCondition
notFullCondition+notFullCondition用的是同一个ReentrantLock
出队列与入队列是阻塞进行的,某个时间点,只有一个线程能获取到锁,然后生产或者消费数据
添加的数据不能为null 队列长度是有限制的
ArrayBlockingQueue的UML
重要属性
重要构造
/**
* @param capacity 容量
* @param fair true 公平锁 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();
}
两个重要的内部方法
enqueue(E x)
dequeue()
这两个方法调用的前提都是线程已经获取到了独占锁,然后才能进行出入队列的操作,具体对条件的队列的阻塞与唤醒,是在前一步完成的。
入队列
put方法 可被中断的
offer方法 尝试入队列 添加成功返回true 添加失败返回false
/**
* 尝试入队列
* @return true 添加成功 false 添加失败
*/
public boolean offer(E e) {
//校验e是不是null 是的话抛出异常
checkNotNull(e);
final ReentrantLock lock = this.lock;
//获取锁
lock.lock();
try {
//当前数据量等于数组的长度 满了 直接返回false
if (count == items.length)
return false;
else {
//在putIndex的位置保存数据
//入队列 然后唤醒消费者者不为空条件队列中的一个 从队头开始
enqueue(e);
return true;
}
} finally {
//释放锁
lock.unlock();
}
}
add方法 添加失败会抛出异常
模板方法,调用的是offer方法
/**
* 入队列
* 最后调用的是自己的offer(E e)
* @param e 要添加的数据
*/
public boolean add(E e) {
return super.add(e);
}
//AbstractQueue中的方法
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
出队列
take方法 可中断的,并且会返回数据项
poll方法 队列为空时,会直接返回null,不为空会返回数据项
remove方法 返回的数据项为null时,会抛出异常
//AbstractQueue中的方法 调用的是poll
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
获取队头数据,但不移除
peek方法 返回的队头数据
element方法 返回的队头数据,数据为null的话会抛出异常,调用的是peek
//AbstractQueue的方法
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
ArrayBlockingQueue完整的源码解析点击这里