【JAVA】多线程案例之阻塞队列---》生产者消费者模型

1.阻塞队列

2.生产者消费者模型

3.标准库阻塞队列

4.自定义阻塞队列的实现

5.阻塞队列实现生产者消费者模型

1.阻塞队列是什么东西?

阻塞就是要等待,

1)当你饿了,下一步是吃饭,但是,你妈妈没做饭,所以你就阻塞等待,等到你妈妈做好饭,你再开始吃饭

2)阻塞队列的底层是一个循环队列,但是要加上阻塞的等待的性质

你要往队列中放元素,如果此时队列中的元素满了,插入元素的这个动作就会停下来等,而不是结束,等到这个满的队列中有元素出队列了,这时你上一步插入元素的动作就会接着往下执行~

反过来也一样,假如队列的元素空了,你准备从这个空队列里取元素,但是没元素给你取你就要停下来等到这个队列被插入元素后,你取元素这个动作才会继续,整个取元素的过程下来不会结束而是在等待

这个就是阻塞队列的原理~


2.生产者消费者模型?

供不应求,供过于求~ ----》 这是生产和消费的关系~

加上阻塞队列的场景就比如包饺子:你爸爸负责擀饺子皮,你和你妈妈负责包饺子(爸爸是生产者,你跟妈妈是消费者),如果你爸爸的速度很快,你跟妈妈才包好10个饺子,爸爸就已经擀好了100张饺子皮,此时你爸爸可以停下来玩会手机休息~反过来,你跟你妈妈包饺子的速度大于爸爸擀饺子皮的速度的话,你跟你妈妈可以停下来等爸爸擀多些饺子皮,你们娘俩再开始包饺子

以上就是生产者消费者模型的简单白话


3.标准库里的阻塞队列?

标准库里面有自带的阻塞队列

【JAVA】多线程案例之阻塞队列---》生产者消费者模型_第1张图片

在普通队列里,我们主要的操控队列的方法有1.出队列 2.入队列 3.获取队首元素

但是阻塞队列中只有前两个方法,没有获取队首元素的方法

public class MyBlockingQueue2 {
    public static void main(String[] args) throws InterruptedException {

        //标准库阻塞队列
        BlockingQueue blockingQueue = new LinkedBlockingQueue(100);//参数代表队列长度
        
        //入队列
        //blockingQueue.offer(1)//没有阻塞功能
        
        blockingQueue.put(1);
        blockingQueue.put(2);
        blockingQueue.put(3);
        
        //出队列
        int n1 = blockingQueue.take();
        System.out.println(n1);
        int n2 = blockingQueue.take();
        System.out.println(n2);
        int n3 = blockingQueue.take();
        System.out.println(n3);
    }
}

4.自定义阻塞队列

实现一个阻塞队列:

        1.实现一个普通循环队列

        2.解决线程安全问题

        3.实现队列的阻塞功能

        4.队列细节问题的解决

 

//阻塞队列的模拟实现

    //1.先写一个普通的循环队列
    //2.解决线程安全问题
    //3.阻塞实现-》1)队列空,出队列阻塞 2)队列满,插入阻塞 -》队列不会出现又满又空的情况
    //小问题:wait可能会被提前唤醒,或者线程之间出现锁竞争导致线程不安全 -》把判断if改成while
class MyBlockingQueue{
    public int[] items = new int[100];
    public volatile int head = 0;
    public volatile int tail = 0;
    public volatile int size = 0;

    //入队列
    public void put(int val) throws InterruptedException {
        synchronized (this){
            //先判满
            while (size >= items.length){
                //此时队列为满,不能再插入元素,要阻塞等待
                this.wait();
            }
            items[tail] = val;
            tail++;
            //入满了
            if(tail >= items.length){
                tail = 0;
            }
            size++;
            this.notify();//这里是唤醒队列为空时的wait,到这里队列已经有元素了
        }
    }

    //出队列
    public Integer take() throws InterruptedException {//把返回值改成Integer可以返回null
        synchronized (this){
            //判空
            while (size == 0){//用while防止wait被提前唤醒,wait会被interrupt唤醒
                //此时队列为空,要阻塞等待,等待到队列加入一个元素
                this.wait();
            }
            int ret = items[head];
            head++;
            if(head >= items.length){
                head = 0;
            }
            size--;
            this.notify();//这里唤醒队列满时的wait
            return ret;
        }
    }

}

1.循环队列---》计数器或者浪费一个空间来实现

2.线程安全---》用synchronized给put和take加锁

3.阻塞实现---》用wait和notify来实现阻塞

4.细节问题----》wait可以被interrupt唤醒/如果在队列为空的情况下,线程1要take,线程2put,线程3take,并且线程1在wait状态,那么当线程2调用notify后,线程1会和线程3产生锁竞争,如果线程3获取锁,并take走了线程2插入的元素,那么线程1会报错

所以把判满和判空的条件改成了while,是为了避免以上情况,如果wait被提前唤醒,那么在判断一遍,不满足条件就继续wait;如果出现了锁竞争,也可以继续wait,避免出现错误


5.生产者消费者模型代码举例

public static void main(String[] args) {

        //生产者消费者模型

        MyBlockingQueue myBlockingQueue = new MyBlockingQueue();

        Thread producer = new Thread(() -> {
            int n = 1;
            while (true){
                try {
                    myBlockingQueue.put(n);
                    System.out.println("生产元素" + n);
                    n++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });

        Thread customer = new Thread(() -> {
            while (true){
                try {
                    int n =myBlockingQueue.take();
                    System.out.println("消费元素" + n);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        producer.start();
        customer.start();
    }

【JAVA】多线程案例之阻塞队列---》生产者消费者模型_第2张图片

 

你可能感兴趣的:(java,开发语言)