Java 生产--消费者模式初探

生产者消费模式 : 顾名思义有生产者、消费者、资源 三个对象。生产者生产资源,消费者消费资源。

类似于工厂流水线,多条生产线(生产者),多条包装线(消费线)。

前面有介绍线程的 等待唤醒机制 我们可以回顾下,一条输入线程(生产者),一条输出线程(消费者),不同线程对同一资源进行操作,只不过操作的工作不一致。

回顾下等待唤醒机制的代码(2条线程)

//资源文件
class Resources
{
   private String name;
   private int count = 1;          
   private boolean flag = false;  //标志位

   //生产函数
   public void  synchronized setName(String name)
   {
     if(flag)
      { try{this.wait();} try(Exception e){} }
      this.name = name + count++;
      System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
      falg = true;
      this.notify();   
   }

   //消费函数
   public void synchronized out()
   {
     if(!flag)
     { try{this.wait();} try(Exception e){} }
     System.out.println(Thread.currentThread().getName()+"  <----消费者--->  "+this.name); 
      flag = false;
      this.notify();
   }

}


// 生产者文件
class Producter implements Runnable
{
   private Resources  res;

   public Producter(Resources res)
   {
      this.res = res;
   }

  //实现run方法
  public void run()
  {
    res.setName("苹果");
  }  
}


//消费者文件
class Consumer implements Runnable
{
   private Resources  res;

   public Consumer(Resources  res)
   {
     this.res = res;
   }

   //实现run方法
   public void run()
   {
      res.out();
   }
}

//main方法
public static void main(String [] args)
{
   //资源
   Resources  res =  new Resources();

  //线程对象
  Thread t1 = new Thread(new Producter(res));
  Thread t2 = new Thread(new Consumer(res));

   //开启线程
   t1.start();
   t2.start();  
}

以上代码运行是们问题的,正常的生产线程生产一个资源,消费线程消费一个资源,有序间隔输出。但是当我们增加线程数继续运行的时候,就发现打印的结果不对了。
在main方法修改如下

    public static void main(String[] args) 
    {

        //创建资源
        Resource res = new Resource();

        //生产者线程
        Thread t1 = new Thread(new Producter(res));
        Thread t2 = new Thread(new Consumer(res));


        //消费者线程
        Thread t3 = new Thread(new Producter(con));
        Thread t4 = new Thread(new Producter(con));


        //开启线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }

.
截取部分输出结果不匹配截图如下
.

这里写图片描述

.
生产2个,只消费了其中1个。或者消费两个,只生产一个。……为什么会出现如上问题呢?我们可以分析下问题
.

假设代码执行流程如下(解释下生产2次,消费一次)

生产线程 : t1、t2
消费线程 :t3、t4

(1) 假设生产者率先获取cpu的执行权,生产者有2个。假设t1先获取执行权,t1判断标志位flag = false,先生产一个(打印一次),后将flag = true,并notify()后。此时t1还持有执行权,继续回来判断flag = true, 符合条件,t1就直接wait了,放弃资格。此时wait的线程就直接进线程池(数据结构为链表结构,先进先出)
(2) t2、t3、t4,假设生产者t2继续获取cpu执行权,判断标志位flag = true,符合条件,t2也wait了,放弃资格。(此时线程池里有t1、t2)
(3) 只剩下t3、t4。 t3先获取执行资格,判断标志位!falg = false,不符合条件,继续往下执行,消费一个。并把标志位设置成falg = false,并notofy()下。后,t3继续享有执行权,回来继续判断标志位 !flag = true 。符合条件,t3就直接wait了。失去执行资格,进入线程池。(此时线程池有t1、t2、t3)。
(4) 上一步骤 t3 执行完后,notify(),此时优先唤醒了生产线程t1。t1 线程在刚wait地方继续往下执行(注意:t1不继续判断标志位了),生产一个。并继续notify(),此时唤醒的是t2线程,t2也不继续判断,继续往下执行,又生产一个。t2唤醒的是消费者t3。t3 就消费一个。此时,就会产生生产两个,只消费一个的现象。

其他情况自己类比解释。

那问题来了? 为什么会造成如上问题呢 ?
根据上述执行步骤分析,我们可以知道,如果t3执行后,唤醒了t1时,t1执行完成后,t1又唤醒t2,如果在唤醒前我们让线程 重新去判断标志位,那就不存在连续生产两个的情况。 也就是说,把 if() ,修改成 while() 。

//资源类文件
class Resource
{
    private String name;

    private int count = 1;

    private boolean flag = false;

    // 生产函数
    public synchronized void setName(String name)
    {
        while(flag) {  //每次执行都判断标记

            try {wait();}catch(Exception e) {}

        }

        this.name = name+"..."+count++;

        System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

        flag = true;

        this.notify();


    }

    // 消费函数
    public synchronized void out()
    {
        while(!flag)   //每次执行都判断标记
        {
            try {wait();}catch(Exception e) {}
        }

        System.out.println(Thread.currentThread().getName()+"  <----消费者--->  "+this.name);

        flag = false;

        this.notify();;
    }

}

.
.

运行下看下打印结果,你会发现,全部线程都wait了。
这里写图片描述

怎么解决呢?Java在Object类里面还有个方法notifyAll()方法。唤醒全部。全唤醒后,他们都会循环去判断标记,只要循环判断标记,就知道什么时候该执行,什么时候该wait。

//资源类文件
class Resource
{
    private String name;

    private int count = 1;

    private boolean flag = false;

    // 生产函数
    public synchronized void setName(String name)
    {
        while(flag) {

            try {wait();}catch(Exception e) {}

        }

        this.name = name+"..."+count++;

        System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

        flag = true;

        this.notifyAll();


    }

    // 消费函数
    public synchronized void out()
    {
        while(!flag)
        {
            try {wait();}catch(Exception e) {}
        }

        System.out.println(Thread.currentThread().getName()+"  <----消费者--->  "+this.name);

        flag = false;

        this.notifyAll();;
    }

}

.
.
运行结果如下(只截取部分截图)
这里写图片描述

总结 :当出现多个生产者和消费者必须
(1) while 循环判断标记
(2) notifyAll () 既唤醒本方又唤醒对方

本文demo地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值