Java中如何利用BlockingDeque实现双端阻塞队列

来源:Python编程网作者:柬埔寨程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《Java中如何利用BlockingDeque实现双端阻塞队列》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Java中如何利用BlockingDeque实现双端阻塞队列》有用,将其分享出去将是对创作者最好的鼓励。

Java中的BlockingDeque是一个支持双端插入和移除操作的阻塞队列接口,它继承了BlockingQueue接口,既具备阻塞队列的特性,又允许从队列的头部和尾部两个方向进行元素的添加和获取操作,非常适合需要两端处理数据的多线程场景。

Java中如何利用BlockingDeque实现双端阻塞队列

BlockingDeque的核心特性

BlockingDeque的核心特性主要体现在两个方面,一方面是双端操作能力,另一方面是阻塞特性。双端操作意味着我们可以在队列的头部添加元素、获取元素,也可以在队列的尾部添加元素、获取元素,相比普通的BlockingQueue只能从尾部添加、头部获取,使用场景更加灵活。

阻塞特性则体现在当队列满时,向队列添加元素的操作会被阻塞,直到队列有空闲空间;当队列为空时,从队列获取元素的操作会被阻塞,直到队列中有新的元素加入,这一特性可以有效解决多线程间的数据传递和线程协作问题,避免忙等带来的性能损耗。

常用实现类LinkedBlockingDeque

Java中BlockingDeque的常用实现类是LinkedBlockingDeque,它是基于链表结构实现的双端阻塞队列,默认情况下队列的容量为Integer的最大值,我们也可以手动指定队列的容量,当队列达到指定容量后,添加操作就会触发阻塞。

和基于数组的ArrayBlockingDeque相比,LinkedBlockingDeque的吞吐量通常更高,因为它的入队和出队操作使用的是不同的锁,而ArrayBlockingDeque使用的是同一把锁,在高并发场景下LinkedBlockingDeque的性能表现更好。

核心方法说明

BlockingDeque提供了一系列用于双端操作的方法,我们可以将这些方法分为四类,不同类别的方法在队列满或空时的处理方式不同:

方法类型头部操作(获取/移除)尾部操作(添加)队列满/空时的处理方式
抛出异常removeFirst()addLast(E e)队列空时移除抛出NoSuchElementException,队列满时添加抛出IllegalStateException
返回特殊值pollFirst()offerLast(E e)队列空时返回null,队列满时添加返回false
阻塞等待takeFirst()putLast(E e)队列空时一直阻塞,队列满时一直阻塞直到有空闲空间
超时等待pollFirst(long timeout, TimeUnit unit)offerLast(E e, long timeout, TimeUnit unit)队列空/满时阻塞指定时间,超时后返回null或false

对应的头部添加和尾部获取方法分别为addFirst、offerFirst、putFirst、pollFirst(超时版本)、removeLast、pollLast、takeLast、offerLast(超时版本),用法和上述对应类型的方法一致。

实现双端阻塞队列示例

下面我们通过一个完整的示例来演示如何利用LinkedBlockingDeque实现双端阻塞队列,示例中模拟两个生产者线程分别从队列的头部和尾部添加元素,两个消费者线程分别从队列的头部和尾部获取元素,展示双端操作的特性。

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

public class BlockingDequeDemo {
    // 创建容量为5的双端阻塞队列
    private static final BlockingDeque<String> deque = new LinkedBlockingDeque<>(5);

    // 头部生产者,向队列头部添加元素
    static class HeadProducer implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 3; i++) {
                    String element = "头部元素-" + i;
                    // 向队列头部添加元素,队列满则阻塞
                    deque.putFirst(element);
                    System.out.println(Thread.currentThread().getName() + " 向头部添加元素:" + element);
                    TimeUnit.MILLISECONDS.sleep(200);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    // 尾部生产者,向队列尾部添加元素
    static class TailProducer implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 3; i++) {
                    String element = "尾部元素-" + i;
                    // 向队列尾部添加元素,队列满则阻塞
                    deque.putLast(element);
                    System.out.println(Thread.currentThread().getName() + " 向尾部添加元素:" + element);
                    TimeUnit.MILLISECONDS.sleep(200);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    // 头部消费者,从队列头部获取元素
    static class HeadConsumer implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 3; i++) {
                    // 从队列头部获取元素,队列空则阻塞
                    String element = deque.takeFirst();
                    System.out.println(Thread.currentThread().getName() + " 从头部获取元素:" + element);
                    TimeUnit.MILLISECONDS.sleep(300);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    // 尾部消费者,从队列尾部获取元素
    static class TailConsumer implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 3; i++) {
                    // 从队列尾部获取元素,队列空则阻塞
                    String element = deque.takeLast();
                    System.out.println(Thread.currentThread().getName() + " 从尾部获取元素:" + element);
                    TimeUnit.MILLISECONDS.sleep(300);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    public static void main(String[] args) {
        // 启动两个生产者线程和两个消费者线程
        new Thread(new HeadProducer(), "头部生产者").start();
        new Thread(new TailProducer(), "尾部生产者").start();
        new Thread(new HeadConsumer(), "头部消费者").start();
        new Thread(new TailConsumer(), "尾部消费者").start();
    }
}

运行上述代码,我们可以看到元素会被分别从队列的头部和尾部添加和获取,当队列满时添加操作会阻塞,当队列空时获取操作会阻塞,完全符合双端阻塞队列的特性。

使用注意事项

在使用BlockingDeque实现双端阻塞队列时,需要注意以下几点。首先,如果需要限制队列容量,一定要手动指定LinkedBlockingDeque的容量,否则默认容量为Integer最大值,可能会导致内存溢出问题。

其次,选择方法时要根据业务场景决定,如果需要必须成功添加或获取元素,就使用阻塞的put和take系列方法;如果允许添加或获取失败,就可以使用返回特殊值的offer和poll系列方法,避免线程长时间阻塞。

最后,双端阻塞队列虽然灵活,但也要合理设计元素的添加和获取方向,避免逻辑混乱,比如在不需要双端操作的场景下,优先使用普通的BlockingQueue即可,不需要过度设计。

BlockingDeque双端阻塞队列Java多线程LinkedBlockingDeque修改时间:2026-07-08 22:27:14

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。