四大经典案例_阻塞队列_生产者消费者模型及java代码实现

举报
bug郭 发表于 2022/09/30 23:37:42 2022/09/30
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

本节要点

  • 了解一些线程安全的案例
  • 学习线程安全的设计模型
  • 掌握单例模式,阻塞队列,生产在消费者模型

阻塞队列

什么是阻塞队列?
顾名思义是队列的一种!
也符合先进先出的特点!
阻塞队列特点:

当队列为空时,读操作阻塞
当队列为满时,写操作阻塞

阻塞队列一般用在多线程中!并且有很多的应用场景!
最典型的一个应用场景就是生产者消费者模型

生产者消费者模型

我们知道生产者和消费者有着供需关系!
而开发中很多场景都会有这样的供需关系!
比如有两个服务器AB
A是入口服务器直接接受用户的网络请求
B应用服务器对A进行数据提供

在通常情况下如果一个网站的访问量不大,那么AB服务器都能正常使用!
而我们知道,很多网站当很多用户进行同时访问时就可能挂!
我们知道,A入口服务器和B引用服务器此时耦合度较高!
当一个挂了,那么另一个服务器也会出现问题!
在这里插入图片描述
而当我们使用生产者消费者模型就很好的解决了上述高度耦合问题!我们在他们中间加入一个阻塞队列即可!

在这里插入图片描述
当增加就绪队列后,我们就不用担心AB的耦合!
并且AB进行更改都不会影响到对方! 甚至将改变服务器,对方也无法察觉!
而阻塞队列还保证了,服务器的访问速度,不管用户量多大! 这些数据都会先传入阻塞队列,而阻塞队列如果满,或者空,都会线程阻塞! 也就不存在服务器爆了的问题!!!
也就是起到了削峰填谷的作用!不管访问量一时间多大!就绪队列都可以保证服务器的速度!

标准库中的就绪队列

我们java中提供了一组就绪队列供我们使用!

BlockingQueue

在这里插入图片描述
BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
put方法用于阻塞式的入队列,
take 用于阻塞式的出队列.
BlockingQueue 也有offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.

//生产着消费者模型
public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        //创建一个阻塞队列
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();
        Thread customer = new Thread(() -> {//消费者
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println("消费元素: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();
        Thread producer = new Thread(() -> {//生产者
            Random random = new Random();
            while (true) {
                try {
                    int num = random.nextInt(1000);
                    System.out.println("生产元素: " + num);
                    blockingQueue.put(num);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者");
        producer.start();
        customer.join();
        producer.join();
    }
}

在这里插入图片描述

阻塞队列实现

虽然java标准库中提供了阻塞队列,但是我们想自己实现一个阻塞队列!

我们就用循环队列实现吧,使用数组!

//循环队列
class MyblockingQueue{
    //阻塞队列
    private int[] data = new int[100];
    //队头
    private int start = 0;
    //队尾
    private int tail = 0;
    //元素个数, 用于判断队列满
    private int size = 0;
    public void put(int x){
        //入队操作
        if(size==data.length){
            //队列满
            return;
        }
        data[tail] = x;
        tail++;//入队
        if(tail==data.length){
            //判断是否需要循环回
            tail=0;
        }
        size++; //入队成功加1
    }
    public Integer take(){
        //出队并且获取队头元素
        if(tail==start){
            //队列为空!
            return null;
        }
        int ret = data[start]; //获取队头元素
        start++; //出队
        if(start==data.length){
            //判断是否要循环回来
            start = 0;
        }
       // start = start % data.length;//不建议可读性不搞,效率也低
        size--;//元素个数减一
        return ret;
    }
}


在这里插入图片描述
我们已经创建好了一个循环队列,目前达不到阻塞的效果!
而且当多线程并发时有很多线程不安全问题!
而我们知道想要阻塞,那不得加锁,不然哪来的阻塞!

//阻塞队列
class MyblockingQueue{
    //阻塞队列
    private int[] data = new int[100];
    //队头
    private int start = 0;
    //队尾
    private int tail = 0;
    //元素个数, 用于判断队列满
    private int size = 0;
    //锁对象
    Object locker = new Object();

    public void put(int x){
       synchronized (locker){//对该操作加锁
           //入队操作
           if(size==data.length){
               //队列满 阻塞等待!!!直到put操作后notify才会继续执行
               try {
                   locker.wait();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           data[tail] = x;
           tail++;//入队
           if(tail==data.length){
               //判断是否需要循环回
               tail=0;
           }
           size++; //入队成功加1
           //入队成功后通知take 如果take阻塞
           locker.notify();//这个操作线程阻塞并没有副作用!
       }
    }
    public Integer take(){
        //出队并且获取队头元素
        synchronized (locker){
            if(size==0){
                //队列为空!阻塞等待 知道队列有元素put就会继续执行该线程
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int ret = data[start]; //获取队头元素
            start++; //出队
            if(start==data.length){
                //判断是否要循环回来
                start = 0;
            }
            // start = start % data.length;//不建议可读性不搞,效率也低
            size--;//元素个数减一
            locker.notify();//通知 put 如果put阻塞!
            return ret;
        }
    }
}
//测试代码
public class Test3 {
    public static void main(String[] args) {
            MyblockingQueue queue = new MyblockingQueue();
            Thread customer = new Thread(()->{
                int i = 0;
                while (true){
                    System.out.println("消费了"+queue.take());
                }
            });

                    Thread producer = new Thread(()->{
                        Random random = new Random();
                        while (true){
                            int x = random.nextInt(100);
                            System.out.println("生产了"+x);
                            queue.put(x);
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    customer.start();
                    producer.start();
    }
}

在这里插入图片描述
可以看到通过waitnotify的配和,我就实现了阻塞队列!!!

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。