生产者消费者模型(多线程并发操作可能会引发的一系列问题)

前言:

多线程并发操作时,有可能会产生两个线程同时操作同一个数据的情况,例如仓库只剩下一件货物,两个消费者同时取这一件货物,这个时候就会产生问题,这也就是所谓的线程不安全

类的设计

  • 仓库类(包含一个集合 因为我们要观察运行中产生的问题,所以使用ArrayList集合)
  • 生产者类(包含向集合中添加元素的方法)
  • 消费者类(包含向集合中取出元素的方法)

以下为代码实现

仓库类

public class Warehouse {
    //私有的集合(仓库)
    private List<String> list = new ArrayList<String>();

    //向集合(仓库)添加元素的方法
     public synchronized void add(){
        // 如果集合中的数据个数还没有到20那么就向集合中添加一个元素
        if(list.size()<20){
            list.add("a");
        }else{
            try {
                //this.notifyAll();

                // 如果集合中元素数量大于20,那么就让线程进入等待状态
                // 这个地方需要注意,this在这里并不是指仓库对象,而是操作仓库对象的那个线程
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //从集合中去元素的方法
     public synchronized void del(){
        if(list.size()>0){
            list.remove(0);
        }else{
            try {
                //this.notifyAll();
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在仓库类中,无论是添加元素还是取出元素,在满足了条件之后,我们需要让线程转换成等待状态,有关其中this.wait() this并不是指仓库对象,而是指一个线程,这点需要注意

生产者类与消费者类只需要重写run()方法(由于生产者和消费者不会只有一个,所以这里我们将两者都继承Thread)
这里我只放了消费者的代码,因为两者代码基本相同,无非一个存,一个取

public class Consumer extends Thread {
    private Warehouse w;
    public Consumer(Warehouse w){
        this.w = w;
    }

    public void run(){
        while(true){
            w.del();
            System.out.println("消费者取出了一件商品");
            try {
                Thread.sleep(300); // 消费者取出商品后休息一下
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里要注意一点,我并没有使用单例模式,而是将仓库对象作为了一个属性,放在了生产者与消费者类中,在最后测试时,创建生产者消费者时,只需要将同一个仓库对象传进去,就可以保证操作的是同一个仓库,当然,如果想使用单例模式也是可以的

测试运行

  • 以上的代码,看似没什么问题,但实际运行的时候会出现生产者把仓库装满之后,进入了等待状态,而消费者一直取物品,取完之后,就没东西可取了,也进入了等待状态,也就相当于是一种假死状态

在这里插入图片描述
在这里插入图片描述
这时候我们可以看到,程序还在运行中,但是两者都进入了等待状态

所以我们必须在生产者或者消费者等待之前,先把对方叫醒 this.notifyAll();

public synchronized void del(){
        if(list.size()>0){
            list.remove(0);
        }else{
            try {
            	//在这里先唤醒其他人,自己再进入等待
                this.notifyAll();
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

执行结果
在这里插入图片描述

可以看到,消费者休息后,生产者会起床开始工作,放满仓库再回去休息,消费者再来取出物品,一直循环下去
不知道大家有没有注意到仓库中的方法中的synchronized关键字,这个关键字就是用来保证线程安全的,我们来看一下没有这个关键字执行的结果是什么

在这里插入图片描述

我们可以看到程序报出了IllegalMonitorStateException的异常,这个异常的意思,就是在调用wait()方法的时候,本来是要告诉生产者线程:你要休息了,可线程突然变成了消费者的线程,这个时候就会出现该异常,所以我们必须要给方法上加上锁(synchronized),这个时候,我们的程序的基本功能才算实现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值