java并发队列之总结(八)
JDK为我们提供了一系列多线程安全的队列. 按实现方式的不同可以分为阻塞队列和非阻塞队列.
阻塞与非阻塞队列
先来看一组类图:
阻塞队列:
非阻塞队列:
从上面的类图可以看出,阻塞与非阻塞就在于是否实现了BlockingQueue这个接口.这个是一个阻塞队列接口.
来看一下Queue类的基本方法:
- add:增加一个元索,一般来说都是直接调用offer()方法,大部分时候只是为了兼容Collection接口.
- offer:添加一个元素并返回true,如果队列已满,则返回false
- remove:移除元素,成功返回true,失败则抛出NoSuchElementException异常
- poll:移除并返问队列头部的元素,如果队列为空,则返回null
- peek:返回队列头部的元素,如果队列为空,则返回null
- element:返回头部元素,如果没有则抛出NoSuchElementException异常
BlockingQueue类继承了Queue类
BlockingQueue增加了2个阻塞方法:
- take: 移除并返回头部元素.如果队列是空的则阻塞.
- put: 添加元素,如果队列满了,则阻塞
以上只是接口在定义的时候设定的一种约束,具体实现还得看具体的实现类.
注意:上面说的阻塞队列,只有调用了阻塞接口中的方法take获取元素或put压入元素时才能起到阻塞的效果,如果使用add或offer压入队列,peek或poll弹出队列是起不到阻塞队列的效果的.当然有界阻塞队列是个例外,因为当达到有界队列长度时也会阻塞.
常见的阻塞与非阻塞队列
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
- ConcurrentLinkedQueue: 一个有链表实现的无界无阻塞队列.
小总结
刚开始学队列的时候,老是被一个无界有界,阻塞,非阻塞给搞的头晕. 原来
- 阻塞队列就是实现了BlockginQueue接口,使用take,put方法则在队列空或者满的时候就阻塞称之为阻塞队列反之则称之为非阻塞队列.
- 有界无界队列就是在初始化队列的时候指定队列的长度,如果超过该长度则阻塞称为有界队列. 无界阻塞队列则初始化了长度但是只要put和take方法不阻塞的情况下,如果生产者一直生产队列自动扩充长度不会受队列长度的影响,称之为无界队列.
总结
队列名称 | 阻塞 | 长度 | 实现方式 | 锁类型 |
---|---|---|---|---|
ArrayBlockingQueue | 阻塞 | 有界 | 数组 | 独占锁 |
LinkedBlockingQueue | 阻塞 | 有界 | 单链表 | 共享锁(读写分离) |
PriorityBlockingQueue | 半阻塞(take阻塞,put不阻塞) | 无界 | 数组实现的最小堆 | 独占锁 |
DelayQueue | 半阻塞(take阻塞,put不阻塞) | 无界 | PriorityQueue队列结构 | 独占锁 |
SynchronousQueue | 阻塞 | 一个元素 | 链表 | CAS锁+LockSupport阻塞 |
ConcurrentLinkedQueue | 非阻塞 | 无界 | 单链表 | CAS锁 |
阻塞队列相似点
- 所有队列的元素都不能为空.为空则抛出空指针异常.
- 无界有界都是人说了算.如果ArrayBlockingQueue设置了队列的长度为Integer.Max则和无界队列一样了.
- 所有阻塞队列都实现了BlockingQueue接口,里面的take方法表示弹出队列头部元素如果空则阻塞;put方法将元素压入队列尾部,如果为队列满了则阻塞(有界队列阻塞,无界队列则不阻塞).
- 这里的都是数组或单链表实现的队列,他们都是先进先出队列.头部读取,尾部压入队列.
- 所谓的阻塞:就是调用take方法弹出元素时如果队列为空则当前线程await.
非阻塞队列
非阻塞队列,没有实现BlockingQueue改接口,所有没有take,put方法.非阻塞队列在压入时无界所以可以一直压入不存在加锁队列长度不足的问题.但是在poll弹出头部元素时,如果队列为空则弹出空值.
重点回顾
- ArrayBlockingQueue:是一个必须设置大小的有界阻塞队列.插入,弹出都会阻塞的一个队列,使用独占锁来保证安全性,多个线程同时只有一个线程能进行插入或弹出操作.
- LinkedBlockingQueue: 是一个有界链表实现的阻塞队列.插入和弹出使用两把锁.多个线程可以有两个线程分别同时做插入和弹出操作.(读写分离);但是在计算size或者移除元素时必须双重加锁,两把锁同时加锁.
- PriorityBlockingQueue(优先级队列):是一个由数组实现的最小堆结构,无界的阻塞队列.队列中的元素带排序功能的一个队列.由于是无界的阻塞队列,所以在put时是不阻塞的,只有在take队列为空时在阻塞.使用独占锁来保证安全性.
- DelayQueue(延时队列):是一个由PriorityQueue实现的无界阻塞队列.同样的无界所以put方法是不阻塞的. DelayQueue里面的元素需要带上计算时间功能,take阻塞有两种情况:一种是队列里面为空时阻塞,另外一种是队列元素时间为到期则同样无法获取同样会阻塞.
- SynchronousQueue:里面使用链表实现,整个队列只有一个元素的阻塞队列,获取后才能put,put后才能take.就是这么任性.
- ConcurrentLinkedQueue:使用单链表实现的无界,无阻塞队列,可以一直压入队列,可以一直弹出队列,队列中没有元素时弹出null.