阻塞队列
1. 什么是阻塞队列?
阻塞队列,顾名思义,它就是一个队列,其在并发情况下,在队列的基础上可以实现**“阻塞”**功能
- 当阻塞队列满的时候,向队列中插入元素的线程将被阻塞,直到其他线程从队列中取出元素
- 当阻塞队列为空的时候,从队列中取出元素的线程被阻塞,直到其他线程向队列中插入元素
如上图所示,如果队列已满,线程1将被阻塞,无法put(插入)元素,如果队列为空,线程2将被阻塞,无法take(取出)元素。
2.为什么使用阻塞队列?
在并发情况下,我们可以不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切阻塞队列会根据线程情况自己决定,就不需要程序员来一直控制。
3. 阻塞队列代码实现
class BlockingQueueDemon
{
private int[] array = new int[10];
//初始化头尾位置及数组大小
private volatile int head = 0;
private volatile int tail = 0;
private volatile int size = 0;
/**
* 阻塞队列入队列
* @param value
*/
public void put(int value) throws InterruptedException
{
synchronized (this)
{
//如果队列满了,阻塞入队列操作,等下面的出队列操作调用notify方法后才可移执行
if (array.length == size)
{
wait();
}
array[tail++] = value;
//保证循环队列,如果队尾已到数组最大长度,将会把下个元素从0开始再次循环
if (tail == array.length)
{
tail = 0;
}
//入队列,队列元素加1
size++;
notify();//唤醒出队列操作
}
}
/**
* 阻塞队列出队列
*/
public int take() throws InterruptedException
{
int ret;
synchronized (this)
{
//如果队列为空,阻塞等待,等到入队列再开始
if (size == 0)
{
wait();
}
ret = array[head++];
//保证循环队列,如果队首已到数组最大长度,将会把下个元素从0开始再次循环
if (head == array.length)
{
head = 0;
}
//出队列,队列元素减1
size--;
notify();//唤醒入队列操作
}
return ret;
}
}
4. 代码分析
如下图所示
- 当队列已满时(array.length = size),如果有线程调用put方法,其将执行wait方法,使当前插入操作的线程阻塞,直到其他线程调用take方法,取出元素后,其中的notify方法将唤醒 阻塞的put方法 线程,才可以执行put后续的插入操作。
- 当队列为空时(size == 0),如果有线程调用take方法,其将执行wait方法,使当前取出操作线程阻塞,直到其他线程调用put方法,插入元素后,其中的notify方法将唤醒 阻塞的take方法 线程,才可以执行take后续的取出操作。
5. JDK内置的阻塞队列
名称 | 描述 |
---|---|
ArrayBlockingQueue | 由数组结构组成的有界阻塞队列 |
LinkedBlockingQueue | 由链表结构组成的有界(但大小默认值为Integer.Max_Value)阻塞队列 |
SynchronousQueue | 不存储元素的阻塞队列,也即单个元素的队列 |
6. 阻塞队列的方法
6.1 插入操作
add(e) :添加元素到队列中,如果队列满了,继续插入元素会报错,IllegalStateException。
offer(e) : 添加元素到队列,同时会返回元素是否插入成功的状态,如果成功则返回 true
put(e) :当阻塞队列满了以后,生产者继续通过 put添加元素,队列会一直阻塞生产者线程,直到队列可用
offer(e,time,unit) :当阻塞队列满了以后继续添加元素,生产者线程会被阻塞指定时间,如果超时,则线程直接退出
6.2 取出操作
remove():当队列为空时,调用 remove 会返回 false,如果元素移除成功,则返回 true
poll(): 当队列中存在元素,则从队列中取出一个元素,如果队列为空,则直接返回 null
take基于阻塞的方式获取队列中的元素,如果队列为空,则 take 方法会一直阻塞,直到队列中有新的数据可以消费
poll(time,unit):带超时机制的获取数据,如果队列为空,则会等待指定的时间再去获取元素返回
null
take基于阻塞的方式获取队列中的元素,如果队列为空,则 take 方法会一直阻塞,直到队列中有新的数据可以消费
poll(time,unit):带超时机制的获取数据,如果队列为空,则会等待指定的时间再去获取元素返回