阻塞队列
阻塞队列 (BlockingQueue)是Java util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空。并发包下很多高级同步类的实现都是基于BlockingQueue实现的。
BlockingQueue接口
方法 | 抛出异常 | 返回特定值 | 阻塞 | 阻塞特定时间 |
---|---|---|---|---|
入队 | add(e) = offer(e) + 异常 | offer(e) | put(e) | offer(e, time, unit) |
出队 | remove() | poll() | take() | poll(time, unit) |
获取队首元素 | element() | peek() | 不支持 | 不支持 |
应用场景
使用队列实现削峰,平滑过渡到下一秒
- 线程池
- 生产者-消费者模型
- 消息队列
- 缓存系统
- 并发任务处理
JUC包下的阻塞队列
基于数组,基于链表的实现
设计注意点: 用while不用if是为了防止虚假唤醒
- ArrayBlockingQueue
- 有界阻塞队列,存取相互排斥
- 数据结构:静态数组
- 容量固定,没有扩容机制
- 没有元素的位置被null占位
- 存取都使用 ReentrantLock
- 阻塞对象
- notEmpty:出队 count == 0时,进行阻塞
- notFull:入队 count == items.length 时,进行阻塞
- 双指针保证队列的先进先出
- putIndex,到队尾时设置0,唤醒notEmpty
- takeIndex,到队尾时设置0,唤醒notFull
- LinkedBlockingQueue
- 无界阻塞队列,可以指定容量,默认为Integer。MAX_VALUE,存取互不干扰。
- 数据结构:链表,内部类 Node 存储元素。
- 锁分离,存取操作不同的Node
- takeLock
- putLock
- 删除时获取两把锁
- 阻塞对象
- notEmpty:出队 count == 0时,进行阻塞
- notFull:入队 count == capacity 时,进行阻塞
- 入队
- last = last.next = node
- 出队
- first = h.next;head=first;
- DelayQueue
- 一个使用优先级队列实现的无界阻塞队列
- 数据结构:PriorityQueue, 与 PriorityBlockQueue类似,不过没有阻塞功能
- 锁:ReentrantLock
- 阻塞对象:Condition available
- 入队:不阻塞
- 出队
- 为空时阻塞
- 检查堆顶元素过期时间
- 小于等于0则出队
- 大于0则阻塞
- leader线程不为空,阻塞
- 为空,设置当前线程为leader,并按照过期时间进行阻塞
ArrayBlockingQueue | LinkedBlockingQueue | |
---|---|---|
队列大小 | 初始化必须指定大小 | 可以有界,也可以指定 |
存储容器 | 数组 | 以Node作为连接对象的链表 |
新增/删除元素 | 不会生产和销毁额外的对象实例 | 会新增Node对象 |
锁实现 | 读写使用同一ReentrantLock | 使用 takeLock 和 putLock 实现读写分离 |