0
点赞
收藏
分享

微信扫一扫

JUC阻塞队列-ArrayBlockingQueue


ArrayBlockingQueue

文章目录

  • ​​ArrayBlockingQueue​​
  • ​​1. 成员​​
  • ​​2. 基本操作​​
  • ​​2.1 构造方法​​
  • ​​2.2 入队​​
  • ​​2.3 出队​​
  • ​​3. 注意​​

1. 成员

​ArrayBlockingQueue​​是基于数组这种数据结构实现的阻塞队列

final Object[] items;// 存放队列元素的数组
int takeIndex; // 指向出队元素的指针
int putIndex; // 入队指针
int count; // 队列元素数量

阻塞队列实现的关键:一把锁 + 两个等待队列

final ReentrantLock lock; // 入队出队要加锁
private final Condition notEmpty; //
private final Condition notFull; //

2. 基本操作

2.1 构造方法

指定容量构造,默认是非公平锁

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();
}

2.2 入队

元素入队需要加锁,当队列满了则阻塞当前线程,​​offer()​​​入队成功返回​​true​​​,失败直接返回​​false​

public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();// 每次入队都需要加锁
try {
if (count == items.length)//如果队列已满,直接返回false
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();//入队成功则解锁
}
}

在看一个​​add()​​方法,实际调的还是offer,但是如果入队失败了,是直接抛出一个【队列已满】的异常

public boolean add(E e) {
return super.add(e);
}

public boolean add(E e) {
if (offer(e))//实际调的还是offer,但是如果入队失败了,是直接抛出一个【队列已满】的异常
return true;
else
throw new IllegalStateException("Queue full");
}

再看一个​​put()​​方法

public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)// 跟offer方法不同的是,如果队列已满,则会一直阻塞
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}

实际入队方法:

private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;// 入队就是在putindex指针对应位置添加元素后移动putIndex指针
if (++putIndex == items.length)// 如果putIndex到了数组末端,则重新从数组头部开始
putIndex = 0;
count++;
notEmpty.signal(); // 入队成功后,唤醒在等待的队列消费者
}

综上所述:

  • ​offer()​​:入队成功失败都有一定的返回值
  • ​add()​​​:实际调的就是​​offer()​​​,但是入队成功返回的是​​true​​,失败的时候是直接抛出异常
  • ​put()​​​:​​put()​​如果队列已满,则会一直阻塞

2.3 出队

出队和入队一样,都需要操作前先加锁

​poll()​​:如果队列是空的,直接返回null,否则执行出队

public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();//如果队列是空的,直接返回null,否则执行出队
} finally {
lock.unlock();
}
}

在看一个出队方法:​​take()​

public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();// 和put一样,队列为空时,一直阻塞
return dequeue();
} finally {
lock.unlock();
}
}

实际出队方法:

private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];// 跟入队一样,出队操作的是takeIndex指向的元素
items[takeIndex] = null; // null to help GC
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();// 出队完成,唤醒在等待的生产者
return x;
}

3. 注意

enqueue()​dequeue()​操作的时候,调用​signal​而不是调用​signalAll​是因为什么?

  • ​signalAll()​​​唤醒全部等待的线程,​​signal()​​随机唤醒一个等待的线程,因为每次只能一个线程获取到锁执行操作,如果唤醒全部线程则会增加竞争锁的概率从而降低了效率


举报

相关推荐

0 条评论