阻塞队列(BlockingQueue)是一种支持两个附加操作的队列:
-
支持阻塞的插入:队列满时,队列阻塞插入元素的线程,直到队列不满
-
支持阻塞的溢出:队列空时,获取元素的线程等待队列变为非空
常见于生产者和消费者场景 -
阻塞队列不可用后,有四种处理方式:
处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e, time,unit) |
移除方法 | remoe(e) | poll() | take() | poll(time, unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
- 抛出异常:插入抛出IllegalStateException(); 获取抛出NoSuchElementException
- 返回特殊值,插入成功返回true;移除,有就取出,没有返回null
- 一直阻塞
- 超时退出
无界阻塞队列永远不满,put和offer方法永远不阻塞,offer永远返回true
1、 Java中的阻塞队列
Java中有7中阻塞队列:
-
ArrayBlockingQueue:数组结构的有界阻塞队列
- FIFO排序
- 默认不保证公平访问
- 设置公平访问用可重入锁实现的,会降低吞吐量
-
LinkedBlockingQueue:链表结构的有界阻塞队列
- 默认和最大长度为Integer.MAX_VALUE
- FIFO排序
-
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- 默认自然排序,可自定义compareTo()方法,或初始化时指定构造餐宿Comparator指定排序,
- 不能保证同优先级元素的顺序
-
DelayQueue:使用优先级队列实现的无界阻塞队列
- 支持延时获取元素
- 用PriorityQueue实现
- 队列中元素必须实现Delay接口
- 创建对象时初始化基本数据
- 实现getDelay方法,返回元素还需要延长多长时间,单位纳秒
- 实现compareTo方法指定元素顺序
- 创建元素时可以指定多久才能获取当前元素
- 元素没有达到延时时间就阻塞当前线程
-
SynchronousQueue:不存储元素的阻塞队列
- 一个put操作必须等待一个take操作,否则不能添加元素
- 默认非公平
- 支持公平访问
- 吞吐量高于ArrayBlockingQueue和LinkedBlockingQueue
-
LinkedTransferQueue:链表结构的无界阻塞队列
- 多了transfer方法:如果有消费者等待,把生产者传入的元素直接传输过去;如果没有等待,把元素存放在tail节点,直到被消费才返回
- 多了tryTransfer方法:试探能否直接传给消费者,如果没有消费者等待,返回false。默认立即返回,可以调用重构的方法,超时等待。
-
LinkedBlockingDeque:链表结构的双向阻塞队列
-多了addLast,offerFirst,offerLast,peekFirst,peekLast等双向队列特有的方法。
2、 阻塞队列实现原理
- 通知模式:ArrayBlockingQueue用了Condition实现:
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 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();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
- 往队列插入元素,队列不可用,通过LockSupport.park(this)来阻塞生产者
- 调用setBlocker前先保存将要阻塞的线程,然后unsafe.park阻塞当前线程
网友评论