一、概念
- 基于ReentrantLock实现的阻塞队列,读写都共用同一把锁
- 底层是一个数组,也就是说,初始化的时候必须要给一个数组长度(不是一个无界队列)
- 先进先出
- 默认是一个非公平,可设置为公平
- Condition的阻塞和唤醒:
- 阻塞其实就是放在Condition的自己的单项链表里面,当Thread封装成node。
- 唤醒其实就是将Condition的链表node拿出来,然后放在AQS的阻塞队列里面,然后等待被唤醒
二、使用
写操作
- add(E e)
- 队列中添加数据,如果队列满了, 会抛出异常new IllegalStateException(“Queue full”)
底层调用offer方法 - 返回值boolean
- offer(E e)
- 队列中添加数据,如果队列满了,不会抛出异常,会返回false
- 返回值boolean
- offer(E e, long timeout, TimeUnit unit)
- 队列中添加数据,如果队列满了,会阻塞当前线程timeout时间
- 返回值boolean
- put(E e)
- 队列中添加数据,如果队列满了,会阻塞当前线程,直到有数据或者被打断
- 无返回值
读操作
- remove()
- 获取队列中数据,如果队列中没有数据,则抛出异常throw new NoSuchElementException()
底层调用poll方法 - 返回值E
- poll()
- 获取队列中数据,如果队列中没有数据,则返回null
- 返回值E
- poll(long timeout, TimeUnit unit)
- 获取队列中数据,如果队列中没有数据,则阻塞当前线程timeout时间
- 返回值E
- take()
- 获取队列中数据,如果队列中没有数据,会阻塞当前线程,直到有数据
- 返回值E
三、源码(jdk1.8)
构造方法
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();
}
public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock();
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();
}
}
重要属性
final Object[] items;
int takeIndex;
int putIndex;
int count;
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
transient Itrs itrs = null;
- putIndex:记录下一次要给那个位置放数据
- takeIndex:记录下一次要从那个位置获取数据
- 如果index到达数组的尾端,那么就将index=0,这样其实就是实现了先进先出了啊
offer(E e)
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();
}
}
private void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
add(E e)
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
offer(E e, long timeout, TimeUnit unit)
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
put(E e)
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();
}
}
poll()
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
final Object[] items = this.items;
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
reomve()
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
poll(E e, long timeout, TimeUnit unit)
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 dequeue();
} finally {
lock.unlock();
}
}
take(E e)
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
四、总结流程(个人总结,可能有误)
- ArrayBlockingQueue其实就是用ReentrantLock+2个condition实现的生产消费队列啊。
- offer/poll
- offer:先看队列中的count跟items.length是否相等,如果相等,则返回false。不等则插入
- poll:先看队列中的count==0,如果相等,则返回null。如果不等则返回数据呗
- offer(E e, long timeout, TimeUnit unit) / poll(E e, long timeout, TimeUnit unit)
- offer:先看队列中的count跟items.length是否相等
- 如果相等,则阻塞timeout时间,然后在唤醒,尝试一下。如果失败了就false。
- 如果不等,则插入
- poll:先看队列中的count==0
- 如果相等,则阻塞timeout时间,然后在唤醒,尝试一下。如果失败了就返回null
- 如果不等,则返回数据呗
- put/take
- put:先看队列中的count跟items.length是否相等
- 如果相等,则阻塞,直到被唤醒或者被打断
- 如果不等,则插入
- take:先看队列中的count==0
- 如果相等,则阻塞,直到被唤醒或者被打断
- 如果不等,则返回数据呗