JAVA 线程池 BlockingQueue详解
BlockingQueue
概述
BlockingQueue 是 Java 中的一个接口,它代表了一个线程安全的队列,不仅可以由多个线程并发访问,还添加了等待/通知机制,以便在队列为空时阻塞获取元素的线程,直到队列变得可用,或者在队列满时阻塞插入元素的线程,直到队列变得可用。
boolean add(E e) :将元素添加到队列尾部,如果队列满了,则抛出异常 IllegalStateException。
boolean offer(E e):将元素添加到队列尾部,如果队列满了,则返回 false。
void put(E e):将元素添加到队列尾部,如果队列满了,则线程将阻塞直到有空间。
offer(E e, long timeout, TimeUnit unit):将指定的元素插入此队列中,如果队列满了,则等待指定的时间,直到队列可用。
take():检索并删除此队列的头部,如有必要,则等待直到队列可用; 一般会上锁保证只有一个线程take到头部
poll(long timeout, TimeUnit unit):检索并删除此队列的头部,如果需要元素变得可用,则等待指定的等待时间。
boolean remove(Object o):从队列中删除元素,成功返回true,失败返回false
E poll():检索并删除此队列的头部,如果此队列为空,则返回null。
E element():检索但不删除此队列的头部,如果队列为空时则抛出 NoSuchElementException 异常;
peek():检索但不删除此队列的头部,如果此队列为空,则返回 null.
BlockingQueue 接口的实现类有 ArrayBlockingQueue、DelayQueue、LinkedBlockingDeque、LinkedBlockingQueue、LinkedTransferQueue、PriorityBlockingQueue、SynchronousQueue 等
ArrayBlockingQueue:
基于数组的有界阻塞队列:
- 有界:ArrayBlockingQueue 的大小是在构造时就确定了,并且在之后不能更改。这个界限提供了流量控制,有助于资源的合理使用。构造必须指定初始值
- FIFO:队列操作符合先进先出的原则。
- 当队列容量满时,尝试将元素放入队列将导致阻塞;尝试从一个空的队列取出元素也会阻塞。
ArrayBlockingQueue 并不能保证绝对的公平,所谓公平是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 ArrayBlockingQueue。
这是因为还有其他系统级别的因素,如线程调度,可能会影响到实际的执行顺序。如果需要公平的 ArrayBlockingQueue,可在声明的时候设置公平标志为 true
ArrayBlockingQueue内部使用了ReentrantLock和对应的Condition完成阻塞需求
LinkedBlockingQueue:
基于链表的线程安全的阻塞队列:
- 可以在队列头部和尾部进行高效的插入和删除操作。
- 当队列为空时,取操作会被阻塞,直到队列中有新的元素可用。当队列已满时,插入操作会被阻塞,直到队列有可用空间。
- 可以在构造时指定最大容量。如果不指定,默认为 Integer.MAX_VALUE,这意味着队列的大小受限于可用内存。
ArrayBlockingQueue和LinkedBlockingQueue:
相同点:
都是通过 Condition 通知机制来实现可阻塞的插入和删除。
不同点:
- ArrayBlockingQueue 基于数组实现,而 LinkedBlockingQueue 基于链表实现;
- ArrayBlockingQueue 使用一个单独的 ReentrantLock 来控制对队列的访问,而 LinkedBlockingQueue 使用两个锁(putLock 和 takeLock),一个用于放入操作,另一个用于取出操作。这可以提供更细粒度的控制,并可能减少线程之间的竞争。
PriorityBlockingQueue:
PriorityBlockingQueue 是一个具有优先级排序特性的无界阻塞队列。元素在队列中的排序遵循自然排序或者通过提供的比较器进行定制排序。可以通过实现 Comparable 接口来定义自然排序。
SynchronousQueue:
SynchronousQueue 是一个非常特殊的阻塞队列,它不存储任何元素。每一个插入操作必须等待另一个线程的移除操作,反之亦然。因此,SynchronousQueue 的内部实际上是空的,但它允许一个线程向另一个线程逐个传输元素。即投递一个任务必须等有人来接受任务才可以返回。
LinkedTransferQueue:
LinkedTransferQueue 是一个基于链表结构的无界传输队列,实现了 TransferQueue 接口,它提供了一种强大的线程间交流机制。它的功能与其他阻塞队列类似,但还包括“转移”语义:允许一个元素直接从生产者传输给消费者,如果消费者已经在等待。如果没有等待的消费者,元素将入队。
transfer(E e),将元素转移到等待的消费者,如果不存在等待的消费者,则元素会入队并阻塞直到该元素被消费。
tryTransfer(E e),尝试立即转移元素,如果有消费者正在等待,则传输成功;否则,返回 false。
LinkedBlockingDeque:
LinkedBlockingDeque 是一个基于链表结构的双端阻塞队列。它同时支持从队列头部插入和移除元素,也支持从队列尾部插入和移除元素。因此,LinkedBlockingDeque 可以作为 FIFO 队列或 LIFO 队列来使用。
addFirst(E e), addLast(E e): 在队列的开头/结尾添加元素。
takeFirst(), takeLast(): 从队列的开头/结尾移除和返回元素,如果队列为空,则等待。
putFirst(E e), putLast(E e): 在队列的开头/结尾插入元素,如果队列已满,则等待。
pollFirst(long timeout, TimeUnit unit), pollLast(long timeout, TimeUnit unit): 在队列的开头/结尾移除和返回元素,如果队列为空,则等待指定的超时时间。
DelayQueue
DelayQueue 是一个无界阻塞队列,用于存放实现了 Delayed 接口的元素,这些元素只能在其到期时才能从队列中取走。这使得 DelayQueue 成为实现时间基于优先级的调度服务的理想选择。