Android开发

java 中的阻塞队列

2021-01-26  本文已影响0人  __Y_Q

一. 什么是队列

队列是一种特殊的线性表, 特殊之处在于它只允许在表的前端 (front) 进行删除操作, 而在表的后端 (rear) 进行插入操作. 和栈一样, 队列是一种操作受限制的线性表. 进行插入操作的端称为队尾, 进行删除操作的端称为对头.

在队列中插入一个队列元素称为入队, 从队列中删除一个队列元素称为出队. 因为队列只允许在一端插入, 在另一端删除, 所以只有最早进入队列的元素才能最先从队列中删除. 故队列又称为先进先出 (FIFO - first in first out) 线性表.


队列示意图.png

 

二. 什么是阻塞队列

简单来说就是支持阻塞插入和阻塞移除的队列就可被称为阻塞队列.

阻塞队列常用于生产者和消费者的场景, 生产者是向队列里添加元素的线程, 消费者是从队列里取元素的线程. 阻塞队列就是生产者用来存放元素, 消费者用来获取元素的容器.

在线程世界里, 生产者就是生产数据的线程, 消费者就是消费数据的线程. 在多线程开发中, 如果生产者处理速度很快, 而消费者处理速度很慢, 那么生产者就必须等待消费者处理完, 才能继续生产数据. 同样道理, 如果消费者的处理能力大于生产者, 那么消费者也就必须等待生产者.

为了解决这种生产消费能力不均衡的的问题, 便有了生产者和消费者模式. 生产者和消费者通过一个容器来解决生产者和消费者的强耦合问题. 生产者和消费者彼此之间不直接通信, 而是通过阻塞队列来进行通信, 所以生产者生产完数据之后不用等待消费者处理, 直接丢给阻塞队列. 消费者则不需要找生产者要数据, 而是直接从阻塞队列里取. 阻塞队列就相当于一个缓冲区, 平衡了生产者和消费者的处理能力.
 

三. 常用阻塞队列

JDK 为我们提供了一个继承自 Queue 接口的 BlockingQueue 接口 , 常用的阻塞队列几乎都实现了此接口. 这些方法并非都是阻塞类的方法, 还包含了非阻塞类的方法. 这两种方法基本都是成对出现的.例如插入和获取.

Queue BlockingQueue
方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add(E) offer(E) put(E) offer(E, long, TimeUnit)
移除方法 remove() poll() take() poll(long, TimeUnit)
检查方法 element() peek()

真正体现了阻塞的两个方法就是 put(E) / take()

 

JDK 中实现了 BlockingQueue 接口的阻塞队列如下图所示

image.png

以上的阻塞队列都实现了 BlockingQueue 接口, 同时也都是线程安全的. (其中 LinkedBlockingDeque 实现了 BlockingDeque 接口. LinkedTransferQueue 实现了 TransferQueue 接口).

有界队列与无界队列的概念
1. ArrayBlockingQueue
2. LinkedBlockingQueue
3. PriorityBlockingQueue
4. DelayQueue
5. SynchronousQueue
6. LinkedTransferQueue
7. LinkedBlockingDeque
Array 实现的队列和 Linked 实现队列的区别
  1. 队列中锁的实现不同
    • ArrayBlockingQueue实现的队列中的锁是没有分离的, 即生产和消费用的是同一个锁
    • LinkedBlockingQueue 实现的队列中的锁是分离的, 即生产用的是 putLock, 消费是 takeLock.
  2. 在生产或消费时操作不同
    • ArrayBlockingQueue 实现的队列中在生产和消费的时候, 是直接将枚举对象插入或移除的.
    • LinkedBlockingQueue 实现的队列中在生产和消费的时候, 需要把枚举对象转换为 Node<E> 进行插入或者移除. 会影响性能.
  3. 队列大小初始化方式不同
    • ArrayBlockingQueue 实现的队列中必须制定队列的大小.
    • LinkedBlockingQueue 实现的队列中可以不指定队列的大小, 但是默认是 Integer.MAX_VALUE.

在 Android 开发中会使用到的估计也就只有 ArrayBlockingQueueLinkedBlockingQueue. 剩下的几种感兴趣的也可以了解一下.

上一篇下一篇

猜你喜欢

热点阅读