1.BlockingQueue接口
继承了Queue接口,为队列的一种类型,区别于队列的2种操作为:
(1)put:队列满时,队列阻塞插入元素线程
(2)take:队列阻塞时,获取元素线程阻塞
思考:阻塞队列是否有容量限制?
看种类,有界队列以ArrayBlockingQueue为主;无界以LinkedBlockingQueue为主;
2. ArrayBlockingQueue
最典型的有界队列
由数组存储元素,初始化时需指定大小;
利用ReentrantLock实现线程安全;
最佳实践:生产者与消费者速度基本匹配时使用;
(1)入队逻辑:
void put(E e) {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
notfull.await(); // 队列满,阻塞
}
enqueue(e);
} finally {
lock.unlock();
}
}
(2)出队逻辑:
void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length) {
putIndex = 0; // 队列满时,指针归0,相较于删除O(n),时间消耗为O(1)
}
count++;
notEmpty.signal(); // 唤醒消费线程,开始消费
}
3. LinkedBlockingQueue
基于链表实现的阻塞队列,为无界队列
3.1 重要特性:读写锁分离
final ReentrantLock takeLock, putLock分别控制进出
Conditional notEmpty, notFull作为进出条件队列
3.2 使用场景
(1)线程池重要组成:读写锁分离,高效率
4. LinkedBlockingDeque
类似LinkedBlockingQueue;区别在于该队列为双向队列;并可控制出入队规则
5. synchronousQueue同步队列
一个没有数据缓冲的BlockingQueue,容量为0,不会为队列中的元素维护储存空间,仅负责多线程的交换
(1)特性:提供公平/非公平两种锁(栈/队列)
(2)使用流程,以队列为例:
入队:构造Node节点入队,并阻塞当前线程
出队:将队首/尾节点唤醒,进行出队
(3)应用场景:对账及批跑
对应一个生产者, 仅有一个消费者:一对一处理关系
如在线程池中,不确定生产者请求数量,且需要实时处理任务,可选择该队列为每个生产者分配一个消费者
例如:在Executor.newCachedThreadPool采用了该种思路
6. PriorityBlockingQueue 优先级队列
一种无界的基于数组优先级的阻塞队列
(1)使用场景:电商抢购,会员级别高的先出队列
(2)数据结构:二叉堆(大顶堆,小顶堆)
(3)相关代码:
7. 延迟队列
(1)实现:由优先级队列
(2)使用场景:超时关单,任务的超时处理,mq挂掉后的重启
网友评论