阻塞式队列

目录

1.阻塞队列是什么

2.阻塞队列中一个最重要的应用场景 :生产者消费者模型

 3.Java标准库中阻塞队列的简单使用

4.阻塞队列的实现


1.阻塞队列是什么

阻塞队列是一种特殊的队列,也遵守"先进先出"的原则。这里注意不要和操作系统内核里,表示阻塞状态的 PCB 的那个链表混为一谈了!!

阻塞队列能够保证线程安全,它有以下特性:

🍃1.当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素。

🍃2.当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素。


2.阻塞队列中一个最重要的应用场景 :生产者消费者模型

什么是生产者消费者模型呢??

🍁 【举例】

 按照上图中的方案二包饺子,会出现两种情况:

🍃1.生产者擀皮速度惊人,还没等消费者使用,盖帘就摆满了,所以它就停下来休息一会。

🍃2.生产者生产的速度跟不上消费者消耗擀皮的速度,于是就出现,盖帘里没有擀皮的情况,于是消费者就等待休息一会。

我们把这种模型就叫做生产者消费者模型!!

生产者消费者模型的优点

🍁优点一:可以做到更好的"解耦合"

🍁优点二:能够做到"削峰填谷",提高整个系统的抗风险能力!!

 所以我们就需要利用生产者消费者模型来解决这种问题!!


 3.Java标准库中阻塞队列的简单使用

public class TestDemo1 {
    public static void main(String[] args) {
        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();

        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(() -> {
            int n = 0;
            while(true) {
                try {
                    System.out.println("生产元素: " + n);
                    blockingQueue.put(n);
                    n++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();
    }
}


4.阻塞队列的实现

🍃基于数组的方式来实现

🍃两个核心方法put入队列)和 take出队列

🍁【实现代码】

public class MyBlockingQueue {
    //假定数组最大容量为 1000
    private int[] elem = new int[1000];
    //队头
    private int front = 0;
    //队尾
    private int rear = 0;
    //阻塞队列中有效数据个数
    volatile private int size = 0;

    //入队列
    public void put(int value) throws InterruptedException {
        synchronized (this) {
            while(size == elem.length) {
                //队列满了,入队阻塞等待
                this.wait();
            }
            elem[rear] = value;
            rear++;
            if(rear == elem.length) {
                //如果 rear 到达数组末尾,就让它重新从 0 开始
                rear = 0;
            }
            size++;
            //插入元素后,就可以把 出队列 唤醒了
            this.notify();
        }
    }
    //出队列
    public Integer take() throws InterruptedException {
        int ret = 0;
        synchronized (this) {
            while(size == 0) {
                //队列为空,出队阻塞等待
                this.wait();
            }
            ret = this.elem[front];
            front++;
            if(front == elem.length) {
                front = 0;
            }
            size--;
            //取走元素后,就可以把 入队列 唤醒了
            this.notify();
        }
        return ret;
    }
}

🍁【基本步骤】

🍃1.通过 "循环队列" 的方式来实现;

🍃2.使用 synchronized 进行加锁控制; 

🍃3.put 插入元素的时候, 判定如果队列满了, 就进行 wait

🍃4.take 取出元素的时候, 判定如果队列为空, 就进行 wait

🍁【重点分析】

🍃1.出队,入队,以及元素个数 ++,-- 等等都涉及到线程安全问题,判空、判满,里面又用到了 wait(),而 wait() 方法的第一步就是释放锁,所以 synchronized 要将它们全部包裹住。

🍃2.理解队列为空时,出队需等待,插入元素后,即可唤醒出队;队列为满时,插入队列需等待,取出元素后,即可唤醒入队

🍃3.入队和出队操作涉及到了读和写,给 size 变量加上 volatile 防止编译器优化,指令重排序影响 size 的值。

🍃4.为什么判空判满时需要加上循环,结合下图理解:

🍁【测试代码】

public class Test {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue();
        Thread customer = new Thread(() -> {
            while(true) {
                try {
                    //消费元素
                    int value = queue.take();
                    System.out.println("消费: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        customer.start();
        Thread producer = new Thread(() -> {
            int value = 0;
            while(true) {
                try {
                    //生产元素
                    queue.put(value);
                    System.out.println("生产: " + value);
                    value++;
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();
    }
}

🍁【分享掌握技巧】

1.理解如何使用;

2.理解内部的实现原理;

3.能够模拟实现;

4.能给别人清楚的讲出来。(写博客就是在给别人讲)!!


本篇博客就到这里了,谢谢观看!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值