前言
本文主要说明BlockingQueue类、阻塞队列使用的共同父类AbstractQueue的基础知识。后续将会逐渐探索BlockingQueue的所有实现类。
BlockingQueue
概览
1. 不接受null元素。
通过新增方法添加null元素时,会抛出空指针异常。因为null值将用作标记值,来指明poll操作失败。同时null值作为阻塞队列的元素也是无任何意义的。
2. 可以有容量限制,也可以没有。
当作为有界队列时,如果当队列空间不足时,add操作将会抛出IllegalStateException异常,offer将会返回false,put操作将会阻塞直到队列空间可用。
如果不设置容量大小,那么默认值是Integer.MAX_VALUE。此时可以看作无界队列;
虽然队列在逻辑上可以是无界的,但由于资源耗尽(导致OutOfMemoryError ),添加操作可能会失败。
3. 用于生产者-消费者队列,但是同时也支持Collection的操作。
例如删除队列元素操作remove(e),但是这些操作的效率很低,一般仅用于删除某个临时取消的任务。
4. 所有实现都是线程安全的。
但是对于集合中的批量操作方法,如果其实现类没有特殊处理的话,那么addAll、containsAll、retainAll、removeAll等可能不会原子地执行。
5. 队列,整体上都基本准寻先进先出的访问顺序。
在整体上看,各个实现类是符合FIFO的顺序的。
主要实现类
LinkedBlockingQueue
:链表式阻塞队列ArrayBlockingQueue
:数组式阻塞队列PriorityBlockingQueue
:优先级阻塞队列LinkedBlockingDeque
:阻塞型双端队列DelayQueue
:延时阻塞队列SynchronousQueue
:实时同步队列(没有任何存储元素的空间)- …………
BlockingQueue方法的四类形式
BlockingQueue的所有实现类的方法都分为如下四类。相对于其他的集合操作方法,put和take是实现阻塞操作的核心方法,需要重点关注。
Throws exception
:操作未实现时(正常流程下的执行)抛出异常Special value
:根据操作的实际情况,返回特定值,例如null、falseBlocks
:阻塞当前线程,直到当前线程可以成功执行Times out
:尝试指定时间后,放弃执行
Throws exception |
Special value |
Blocks |
Times out |
|
新增 |
add(E e) |
offer(E e) |
put(E e) |
offer(E e, long timeout, TimeUnit unit) |
删除 |
remove() |
poll() |
take() |
poll(long timeout, TimeUnit unit) |
查询 |
element() |
peek() |
BlockingQueue类确定了阻塞队列的整体框架,确定了各个方法的定义,因此也是了解阻塞队列一定要学习的类。
本文中大部分都是从源码注释中解读而来,同时也包含了一些自己的理解,如果表述有误,还望指出。
AbstractQueue
概览
AbstractQueue类,提供了一些Queue操作的骨架实现。
这里提及AbstractQueue的原因是,阻塞队列的大多实现类都继承该类,可以说,这个抽象类,实现了BlockingQueue的大部分通用功能。比如add、remove失败时抛出异常等等,下面会一一提及。
源码解析
这个类基本上搭建起了一个阻塞队列Throws exception形式的框架,它实现了各个抛出异常的方法。
各个方法都比较简单,关键在于了解其具体的实现思路。这对了解各个阻塞队列的实现类很有帮助。
add
public boolean add(E e) { if (offer(e)) return true; else throw new IllegalStateException("Queue full"); }
当队列空间足够时,将直接插入成功。offer方法由具体的子类实现。
如果offer插入元素成功,则add返回true ,否则将抛出IllegalStateException异常。
remove
public E remove() { E x = poll(); if (x != null) return x; else throw new NoSuchElementException(); }
检索并删除队列的队首元素。
它与poll不同之处仅在于,如果此队列为空,它会抛出NoSuchElementException异常。
删除成功后,返回删除的元素。
element
public E element() { E x = peek(); if (x != null) return x; else throw new NoSuchElementException(); }
仅获取队列的队首元素并返回结果,并不进行操作。
与peek不同之处在于,如果此队列为空,它会引发NoSuchElementException异常。
clear
与addAll
public void clear() { while (poll() != null) ; } public boolean addAll(Collection extends E> c) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); boolean modified = false; for (E e : c) if (add(e)) modified = true; return modified; }
遍历添加或删除所有元素,实现挺简单的,不过这里并没有进行单个操作出现异常时的处理。
总结
阻塞队列了解得也差不多了,之前只是简单的学习了如何使用阻塞队列的方法,这次也是打算来一个全面深入的学习吧。知其然知其所以然,对于程序员还是蛮重要的。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。