Java BlockingQueue和BlockingDeque的区别(非常详细)
BlockingQueue 和 BlockingDeque 都是 JUC(java.util.concurrent包)中的接口,用于在多线程环境中处理元素的集合,允许线程安全地从队列中添加和移除元素。
BlockingQueue 和 BlockingDeque 在功能上有很多相似之处,主要的区别在于它们的数据结构特性和操作方法。
BlockingQueue 支持的操作如下表所示:
BlockingQueue 常用于生产者-消费者场景,其中,生产者和消费者可能运行在不同的线程中。生产者将对象放入队列,消费者则从队列中取出对象。当队列为空时,消费者线程会被阻塞,直到有元素可以取出。同样,如果队列是有界的,生产者线程在队列满时会被阻塞,直到队列中有空间可用以插入新元素。
BlockingQueue 只是一个接口,JUC 提供了多种具体实现,常用的几种如下:
BlockingDeque 继承自 BlockingQueue,在 BlockingQueue 的基础上,增加了能够从队列的双端插入和移除元素的能力。这种双端队列支持同时作为标准队列(FIFO)和栈 [LIFO(Last In First Out,后进先出)] 使用。
BlockingDeque 的主要特点和功能如下。
BlockingDeque 提供的主要方法如下表所示:
BlockingDeque 扩展了 BlockingQueue,它继承了 BlockingQueue 的所有功能,同时增加了对双端操作的支持,这种结构非常适用于需要双端访问的场景,如工作窃取算法和某些并发编程模式。JUC 也提供了 BlockingDeque 的具体实现,例如 LinkedBlockingDeque(它是一个基于链表结构的可选有界 BlockingDeque)。
在并发编程中,我们选择哪一种队列取决于具体的需求,例如需要单向队列还是双端队列、大小限制要求、插入和移除操作的公平性要求、排序要求等。
BlockingQueue 和 BlockingDeque 在功能上有很多相似之处,主要的区别在于它们的数据结构特性和操作方法。
Java BlockingQueue
BlockingQueue 代表一个支持阻塞操作的线程安全队列,它扩展了 Queue 接口,增加了能够阻塞或等待队列变为非空时取出元素,以及等待空间变得可用时存入元素的操作。BlockingQueue 支持的操作如下表所示:
方法 | 描述 |
---|---|
add(e) | 在队列的尾部插入指定的元素,成功时返回 true;如果没有可用空间,则抛出 IllegalStateException |
offer(e) | 在队列的尾部插入指定的元素,成功时返回 true;如果没有可用空间,则返回 false |
offer(e, time, unit) | 将元素插入队列的尾部,等待指定的时间以让空间变得可用;时间单位由第三个参数指定 |
put(e) | 将元素插入队列的尾部,如果队列满,则等待空间变得可用 |
remove() | 移除队列的头部元素,如果队列为空,则抛出 NoSuchElementException |
poll() | 移除并返回队列头部的元素,如果队列为空,则返回 null |
poll(time, unit) | 移除并返回队列头部的元素,如果队列为空,等待指定的时间以让元素变得可用,时间单位由第二个参数指定 |
take() | 移除并返回队列头部的元素,如果队列为空,则等待直到有元素可用 |
element() | 返回队列头部的元素,如果队列为空,则抛出 NoSuchElementException |
peek() | 返回队列头部的元素,如果队列为空,则返回 null |
BlockingQueue 常用于生产者-消费者场景,其中,生产者和消费者可能运行在不同的线程中。生产者将对象放入队列,消费者则从队列中取出对象。当队列为空时,消费者线程会被阻塞,直到有元素可以取出。同样,如果队列是有界的,生产者线程在队列满时会被阻塞,直到队列中有空间可用以插入新元素。
BlockingQueue 只是一个接口,JUC 提供了多种具体实现,常用的几种如下:
- ArrayBlockingQueue:一个数组结构的有界队列;
- LinkedBlockingQueue:一个链表结构的可选有界队列;
- LinkedBlockingQueue:一个链表结构的可选有界队列;
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列;
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,其中的元素只有在其指定的延迟到期时才能取出;
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等待一个相应的移除操作,反之亦然;
- LinkedTransferQueue:一个链表结构的无界阻塞队列,实现了 TransferQueue 接口。
Java BlockingDeque
BlockingDeque 代表了一个双端队列(Deque,即 Double-Ended Queue),同时支持阻塞操作。BlockingDeque 继承自 BlockingQueue,在 BlockingQueue 的基础上,增加了能够从队列的双端插入和移除元素的能力。这种双端队列支持同时作为标准队列(FIFO)和栈 [LIFO(Last In First Out,后进先出)] 使用。
BlockingDeque 的主要特点和功能如下。
- 双端操作:提供了在队列的头部和尾部进行插入、移除和检查元素的操作;
- 阻塞操作:当队列为空时,获取操作会阻塞直到队列中有元素;在有界队列中,当队列为满时,插入操作会阻塞直到队列中有可用空间;
- 线程安全:所有的插入、移除和检查操作都是线程安全的,可以在多线程程序中使用而不需要额外的同步措施;
- 可选的固定容量:BlockingDeque 可以是有界的,也可以是无界的。有界的 BlockingDeque 有一个最大容量限制。
BlockingDeque 提供的主要方法如下表所示:
方法 | 描述 |
---|---|
addFirst(e) / offerFirst(e) | 将指定元素插入双端队列的头部,队列已满时,直接抛出异常 |
addLast(e) / offerLast(e) | 将指定元素插入双端队列的尾部,队列已满时,直接抛出异常 |
putFirst(e) / offerFirst(e, time, unit) | 将指定元素插入双端队列的头部,队列已满时,进行阻塞等待 |
putLast(e) / offerLast(e, time, unit) | 将指定元素插入双端队列的尾部,队列已满时,进行阻塞等待 |
removeFirst() / pollFirst() | 移除并返回双端队列的第一个元素,队列为空时,直接抛出异常 |
removeLast() / pollLast() | 移除并返回双端队列的最后一个元素,队列为空时,直接抛出异常 |
takeFirst() / pollFirst(time, unit) | 移除并返回双端队列的第一个元素,队列为空时,进行阻塞等待 |
takeLast() / pollLast(time, unit) | 移除并返回双端队列的最后一个元素,队列为空时,进行阻塞等待 |
getFirst() / peekFirst() | 返回双端队列的第一个元素 |
getLast() / peekLast() | 返回双端队列的最后一个元素 |
BlockingDeque 扩展了 BlockingQueue,它继承了 BlockingQueue 的所有功能,同时增加了对双端操作的支持,这种结构非常适用于需要双端访问的场景,如工作窃取算法和某些并发编程模式。JUC 也提供了 BlockingDeque 的具体实现,例如 LinkedBlockingDeque(它是一个基于链表结构的可选有界 BlockingDeque)。
BlockingQueue和BlockingDeque的区别
BlockingDeque 和 BlockingQueue 的区别主要有以下几个方面:- 数据结构:BlockingDeque 是双端队列;而 BlockingQueue 是单向队列。
- 操作方法:BlockingDeque 提供了更多元的操作接口,支持从双端添加或移除元素;而 BlockingQueue 的操作相对受限。
- 用途和灵活性:BlockingDeque 由于其双向特性,适用于更复杂或需要双向操作的场景;而 BlockingQueue 通常用于简单的生产者-消费者场景。
在并发编程中,我们选择哪一种队列取决于具体的需求,例如需要单向队列还是双端队列、大小限制要求、插入和移除操作的公平性要求、排序要求等。