线程池工作队列
BlockingQueue
在类 Executors
中,我们可以看到不同线程池维护的工作队列是不同的,如newCachedThreadPool
使用的是SynchronousQueue
同步队列,newSingleThreadScheduledExecutor
使用DelayedWorkQueue
,
newFixedThreadPool
和newScheduledThreadPool
使用LinkedBlockingQueue
。它们都是实现了并发包java.util.concurrent
中的BlockingQueue
,下面说说这个接口。
BlockingQueue 阻塞队列
继承于队列 Queue
,遵循先进先出原则(FIFO),队列提供几种基本的操作,添加元素(队尾)、移除元素(队头)、取出队头元素(不移除),每种操作都有两个方法,一种有可能抛异常,一种返回操作成功或失败。
在这个基础上,阻塞队列增加了操作锁,保证了数据安全,当然这个具体实现是在子类中完成,接口仅仅描述方法的特点,还增加两种不同的操作实现。下面描述这四种不同类型的操作:
- 操作,可能抛异常
- 操作,不抛异常(特殊如类转换异常、空指针、参数异常不属于,仅当队列已满不会跑状态异常)
- 无限期阻塞线程直至操作成功
- 有时间限制的操作
添加 | 移除 | 检查 |
add(e) | remove(o) | element() |
offer(e) | poll() | peek() |
put(e) | take() | \ |
offer(e,time,unit) | poll(time,unit) | \ |
以及增加了拷贝 drainTo
,如线程池的 shutdownNow
就是用它完成工作队列的清除以及队列中数据的拷贝,还有其他如对比元素contains
,剩余容量查询remainingCapacity
等。
实现的子类
- ArrayBlockingQueue 数组型阻塞队列
- LinkedBlockingQueue 链表型阻塞队列
- DelayQueue 延时队列
- SynchronousQueue 同步队列
- PriorityBlockingQueue 优先阻塞队列
ArrayBlockingQueue
特点:
- 初始化一定容量的数组
- 使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥
- 是有界设计,如果容量满无法继续添加元素直至有元素被移除
- 使用时开辟一段连续的内存,如果初始化容量过大容易造成资源浪费,过小易添加失败
LinkedBlockingQueue
特点:
- 内部使用节点关联,会产生多一点内存占用
- 使用两个重入锁分别控制元素的入队和出队,用
Condition
- 进行线程间的唤醒和等待
- 有边界的,在默认构造方法中容量是
Integer.MAX_VALUE
- 非连续性内存空间
DelayQueue
特点:
- 无边界设计
- 添加(put)不阻塞,移除阻塞
- 元素都有一个过期时间
- 取元素只有过期的才会被取出
SynchronousQueue
特点:
- 内部容量是0
- 每次删除操作都要等待插入操作
- 每次插入操作都要等待删除操作
- 一个元素,一旦有了插入线程和移除线程,那么很快由插入线程移交给移除线程,这个容器相当于通道,本身不存储元素
- 在多任务队列,是最快的处理任务方式。
PriorityBlockingQueue
特点:
- 无边界设计,但容量实际是依靠系统资源影响
- 添加元素,如果超过1,则进入优先级排序