阻塞队列和生产者消费者模型

简介: 阻塞队列是一种特殊队列,当队列空时,获取元素操作会被阻塞;当队列满时,插入操作会被阻塞。可通过数组模拟实现,使用`wait`和`notify`控制阻塞与唤醒。生产者消费者模型中,阻塞队列作为缓冲区,实现生产者与消费者的解耦,提高系统可维护性和扩展性。

1. 阻塞队列

1.1. 阻塞队列的使用

阻塞队列是一种特殊的队列,相比于普通的队列,它支持两个额外的操作:当队列为空时,获取元素的操作会被阻塞,直到队列中有元素可用;当队列已满时,插入元素的操作会被阻塞,直到队列中有空间可以插入新元素。

当阻塞队列满的时候,线程就会进入阻塞状态:

public class ThreadDemo19 {
    public static void main(String[] args) throws InterruptedException {
        BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(3);
        blockingDeque.put(1);
        System.out.println("添加成功");
        blockingDeque.put(2);
        System.out.println("添加成功");
        blockingDeque.put(3);
        System.out.println("添加成功");
        blockingDeque.put(4);
        System.out.println("添加成功");
    }
}

同时,当阻塞队列中没有元素时,再想要往外出队,线程也会进入阻塞状态

public class ThreadDemo20 {
    public static void main(String[] args) throws InterruptedException {
        BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(20);
        blockingDeque.put(1);
        System.out.println("添加成功");
        blockingDeque.put(2);
        System.out.println("添加成功");
        blockingDeque.take();
        System.out.println("take成功");
        blockingDeque.take();
        System.out.println("take成功");
        blockingDeque.take();
        System.out.println("take成功");
    }
}

1.2. 实现阻塞队列

根据阻塞队列的特性,可以尝试来自己手动实现一下

可以采用数组来模拟实现:

public class MyBlockingDeque {
    private String[] data = null;
    private int head = 0;
    private int tail = 0;
    private int size = 0;
    public MyBlockingDeque(int capacity) {
        data = new String[capacity];
    }
}

接下来是入队列的操作:

public void put(String s) throws InterruptedException {
    synchronized (this) {
        while (size == data.length) {
            this.wait();
        }
        data[tail] = s;
        tail++;
        if (tail >= data.length) {
            tail = 0;
        }
        size++;
        this.notify();
    }
}

由于设计到变量的修改,所以要加上锁,这里调用wait和notify来模拟阻塞场景,并且需要注意wait要使用while循环,如果说被Interrupted打断了,那么就会出现不可预料的错误

出队列也是相同的道理:

public String take() throws InterruptedException {
String ret = "";
synchronized (this) {
    while (size == 0) {
        this.wait();
    }
    ret = data[head];
    head++;
    if (head >= data.length) {
        head = 0;
    }
    size--;
    this.notify();
}
return ret;
}

2. 生产者消费者模型

生产者消费者模型是一种经典的多线程同步模型,用于解决生产者和消费者之间的协作问题。在这个模型中,生产者负责生产数据并将其放入缓冲区,消费者负责从缓冲区中取出数据并进行处理。生产者和消费者之间通过缓冲区进行通信,彼此之间不需要直接交互。这样可以降低生产者和消费者之间的耦合度,提高系统的可维护性和可扩展性。

而阻塞队列可以当做上面的缓冲区:

public class ThreadDemo21 {
    public static void main(String[] args) {
        BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(100);
        Thread t1 = new Thread(()->{
            int i = 1;
            while (true){
                try {
                    blockingDeque.put(i);
                    System.out.println("生产元素:" + i);
                    i++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(()->{
            while (true){
                try {
                    int i = blockingDeque.take();
                    System.out.println("消费元素:" + i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }
}

如果说把sleep的操作放到线程2会怎么样?

线程一瞬间就把阻塞队列沾满了,后面还是一个线程生产,一个线程消费,虽然打印出来的有偏差

相关文章
|
算法 Python
Scipy 中级教程——积分和微分方程
Scipy 中级教程——积分和微分方程【1月更文挑战第5篇】
387 2
支付系统39----支付宝支付,定时查单,每隔30秒执行1次,查询超过5分钟,并且未支付的订单
支付系统39----支付宝支付,定时查单,每隔30秒执行1次,查询超过5分钟,并且未支付的订单
getReader() has already been called for this request
getReader() has already been called for this request
1919 0
getReader() has already been called for this request
|
Android开发
一文分析 Only fullscreen opaque activities can request orientation 报错原因及解决方案
今天在三星S8上遇见一个奇葩问题`Only fullscreen opaque activities can request orientation`,探究一下Android源码,出现这个错误的原因,以及解决方案。
820 0
一文分析 Only fullscreen opaque activities can request orientation 报错原因及解决方案
|
3月前
|
数据采集 人工智能 算法
“脏”数据毁一生?教你用大数据清洗术,给数据洗个“澡”!
“脏”数据毁一生?教你用大数据清洗术,给数据洗个“澡”!
410 4
|
4月前
|
人工智能 安全 Apache
Unity Catalog 三大升级:Data+AI 时代的统一治理再进化
在刚刚落幕的 2025 Databricks Data + AI Summit 上,Databricks 重磅发布了多项 Lakehouse 相关功能更新。其中,面向数据湖治理场景的统一数据访问与管理方案 —— Unity Catalog,迎来了三大关键升级:全面支持 Apache Iceberg、面向业务用户的全新使用体验,以及数据治理与安全能力的持续增强。
|
Ubuntu 虚拟化 Windows
将虚拟机dmesg日志内容通过串口输出到windows下文件中
将虚拟机dmesg日志内容通过串口输出到windows下文件中
390 0
|
敏捷开发 运维 项目管理
一个优秀的PM应该是什么样
【4月更文挑战第14天】一个优秀的PM应该是什么样
|
网络协议 C# C++
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
|
Android开发
Android Studio插件的源文件位置——mac端
Android Studio中我们可以通过菜单栏的`Android Studio --> preferences --> plugins`来查看我们安装的插件。这里介绍下插件的实际安装位置。
下一篇
oss教程