线程-阻塞队列
文章目录
前言
阻塞队列是线程池十分重要的一个部分,当大量的任务到来时,线程池会把多出来的任务,暂存在阻塞队列中。它有好的地方,那它坑的地方你了解吗?
提示:以下是本篇文章正文内容
一、ArrayBlockingQueue简介
是一个数组实现的环形队列,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作。
二、ArrayBlockingQueue队列的原理
利用了Lock锁的Condition通知机制进行阻塞控制。
核心:一把锁,两个条件
//数据元素数组
final Object[] items;
//下一个待取出元素索引
int takeIndex;
//下一个待添加元素索引
int putIndex;
//元素个数
int count;
//内部锁
final ReentrantLock lock;
//消费者
private final Condition notEmpty;
//生产者
private final Condition notFull;
public ArrayBlockingQueue(int capacity, boolean fair) {
...
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
1. 一图看懂put与take源码
1.1 put总结
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();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
//将值放入队列
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
//唤醒生产者
notEmpty.signal();
}
我们在对流程图进行文字的总结
- 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
- 判断阻塞队列是否满了,如果满了,则调用await方法阻塞这个线程,notFull(生产者)挂起,最后释放lock锁,等待被消费者线程唤醒。
- 如果没有满,则调用enqueue方法将元素put进阻塞队列。
- 唤醒一个标记为notEmpty(消费者)的线程。
1.2 take总结
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//自选获取锁
lock.lockInterruptibly();
try {
//如果队列为空,则消费者挂起
while (count == 0)
notEmpty.await();
//获取队列值
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];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
//唤醒生产者
notFull.signal();
return x;
}
我们在对流程图进行文字的总结
- 拿到线程竞争lock锁,拿到了lock锁的线程进入下一步,没有拿到lock锁的线程自旋竞争锁。
- 判断阻塞队列是否为空,如果是空,则调用await方法阻塞这个线程,notEmpty(消费者)挂起,最后释放lock锁,等待被生产者线程唤醒。
- 如果没有空,则调用dequeue方法。
- 唤醒一个标记为notFull(生产者)的线程