生产者消费者模式为什么不用if语句而用while循环

一、生产者消费者模式(只有两个线程一个是生产者线程一个是消费者线程)

重点!!!!!!!看这篇博客的时候首先要描述一下wait()方法的等待机制,当当先线程被wait之后会释放锁,当这个线程被唤醒的时候会继续从wait方法后面的代码继续执行。这个点也是这篇文章的点睛之笔,重中之重。也是为什么不用if用while的原因。

小声bb:我也是翻阅好多文章才知道线程阻塞,被唤醒之后竟然会接着执行线程wait方法后面的代码。我原来竟然一直傻逼的认为这个线程被阻塞之后会重新执行。菜鸟就是我我就是彩笔。

首先看一下只有一个生产者和线程一个消费者线程的情况,这个时候使用if(多个生产者和消费者线程的时候一定要使用while,下面会接着描述)不会出现数据错误,因为只有两个线程调用this.notifyAll()的时候只会唤醒所有的线程,但是这个时候只有一个等待线程。代码如下:

package com.aa.生产者消费者模式;

public class A {

    public static void main(String[] args) {
        Data data = new Data();

        //生产者线程A1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者A1").start();

        //消费者线程B1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B1").start();

    }


}

//这是是共享区域data,里面只有一个属性num
class Data{
    private int num=0;

    //生产者对 num++
    public synchronized void increment() throws InterruptedException {
        
        //我是故意用if的,在这里不会出错,因为只有两个线程,被唤醒的只能是消费者线程,对num--
        if(num!=0){
            this.wait();  //此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他线程
        this.notifyAll();
    }

    //消费者对 num--
    public synchronized void decrement() throws InterruptedException {

        //我是故意用if的,在这里不会出错,因为只有两个线程,被唤醒的只能是生产者线程
        if (num==0){
            this.wait();//此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }

        num--;

        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他等待的线程
        this.notifyAll();
    }
}

这个是程序正常运行的结果:
在这里插入图片描述

二、然后给大家演示一下开始四个线程,两个消费者线程两个生产者线程

此时有生产者A1,生产者A2,消费者B1,消费者B2

package com.aa.生产者消费者模式;

public class A {

    public static void main(String[] args) {
        Data data = new Data();

        //生产者线程A1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者A1").start();

        //消费者线程B1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B1").start();

          new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者A2").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B2").start();

    }


}

//这是是共享区域data,里面只有一个属性num
class Data{
    private int num=0;

    //生产者对 num++
    public synchronized void increment() throws InterruptedException {


        if(num!=0){
            this.wait();  //此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他线程
        this.notifyAll();
    }

    //消费者对 num--
    public synchronized void decrement() throws InterruptedException {

        if (num==0){
            this.wait();//此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }

        num--;

        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他等待的线程
        this.notifyAll();
    }
}

然后给大家看一下运行效果:
在这里插入图片描述前面运行还算正常,后面就不正常了。而且会出现死循环。程序一直运行。主要就是因为使用了if语句当线程阻塞后,被挂起,当其他线程使用notifyAll()之后,假如这个线程被执行了,这个被挂起的线程会接着执行wait()方法后面的代码,对num++或者num--,但是如果使用while替代if之后他会一直判断当前条件满不满足不满足接着被wait()直至不满足while的判断语句时之后才会继续执行,而if是直接执行不会再次进行判断。

三、正确的生产者消费者模式

正确的生产者消费者代码如下:

package com.aa.生产者消费者模式;

public class A {

    public static void main(String[] args) {
        Data data = new Data();

        //生产者线程A1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者A1").start();

        //消费者线程B1
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B1").start();

          new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者A2").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B2").start();

    }


}

//这是是共享区域data,里面只有一个属性num
class Data{
    private int num=0;

    //生产者对 num++
    public synchronized void increment() throws InterruptedException {


        while(num!=0){
            this.wait();  //此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他线程
        this.notifyAll();
    }

    //消费者对 num--
    public synchronized void decrement() throws InterruptedException {

        while (num==0){
            this.wait();//此时线程阻塞,让线程被唤醒之后会重新继续执行后续代码,这也是为什么不用if用while的原因
        }

        num--;

        System.out.println(Thread.currentThread().getName()+"==="+num);
        //唤醒其他等待的线程
        this.notifyAll();
    }
}

程序运行之后的效果如下:
在这里插入图片描述
我刚开始也是不理解while和if在生产者和消费者之间的区别,我也是在经历校招准备面试某公司很长时间失败之后,才偶然发现的。于是奋笔疾书给大家分享一下,希望能够帮助大家顺利找到心仪的公司,小声bb菜鸡找工作真难,一次一次降低自己的标准,唉!愿:心所想,有所成,加油各位!!!冲!!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以为您讲解 Shell 脚本中的生产者消费者模型。 在 Shell 脚本中,可以使用循环和条件语句来实现生产者消费者模型。具体来说,生产者和消费者可以分别使用两个不同的函数来实现,这两个函数可以分别在两个不同的进程中执行。 生产者函数的主要作用是生成数据并将其存储到共享缓冲区中,而消费者函数则从共享缓冲区中获取数据并进行相应的处理。在这个过程中,需要使用互斥锁和信号量来确保数据的正确性和一致性。 下面是一个简单的 Shell 脚本生产者消费者模型的示例代码: ```bash #!/bin/bash # 定义共享缓冲区 BUFFER_SIZE=5 buffer=() # 定义互斥锁和信号量 mutex=1 empty=${BUFFER_SIZE} full=0 # 定义生产者函数 producer() { while true; do # 申请 empty 信号量 if [[ ${empty} -gt 0 ]]; then # 申请互斥锁 if [[ ${mutex} -eq 1 ]]; then # 生产数据并存储到共享缓冲区 item=$(date +%s) buffer+=(${item}) echo "生产者生产了数据:${item}" # 释放互斥锁 mutex=0 # 更新 empty 和 full 信号量 empty=$((${empty}-1)) full=$((${full}+1)) fi fi # 延时一定时间 sleep 1 done } # 定义消费者函数 consumer() { while true; do # 申请 full 信号量 if [[ ${full} -gt 0 ]]; then # 申请互斥锁 if [[ ${mutex} -eq 1 ]]; then # 获取共享缓冲区中的数据并进行处理 item=${buffer[0]} buffer=("${buffer[@]:1}") echo "消费者消费了数据:${item}" # 释放互斥锁 mutex=0 # 更新 empty 和 full 信号量 empty=$((${empty}+1)) full=$((${full}-1)) fi fi # 延时一定时间 sleep 1 done } # 启动生产者和消费者进程 producer & consumer & ``` 在这个示例代码中,我们定义了一个大小为 5 的共享缓冲区,以及一个互斥锁和两个信号量。生产者函数和消费者函数分别使用了无限循环来不断地执行生产和消费操作。其中,在生产者函数中,我们使用了 `date +%s` 命令来生成一个时间戳作为数据,并将其存储到共享缓冲区中;在消费者函数中,我们从共享缓冲区中获取了第一个数据,并将其从缓冲区中删除。 需要注意的是,这个示例代码只是一个简单的演示,实际上在真实的生产环境中,需要考虑更多的细节和问题,例如如何处理缓冲区溢出、如何在生产者和消费者之间进行数据同步等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

想成为大牛的渣渣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值