写点什么

【Java 深入学习】一个经典问题 - 消费者和生产者问题 - 下

作者:Geek_65222d
  • 2022-10-22
    河南
  • 本文字数:2951 字

    阅读完需:约 10 分钟


初版代码示例

class SynStack{  private char[] data = new char[6];  private int cnt = 0;    public void push(char ch)  {    data[cnt]=ch;    ++cnt;    System.out.println("1111");    }  public char pop()  {    --cnt;    System.out.printf("2222");    return data[cnt];    }}class Producer implements Runnable{  private SynStack ss=null;  public Producer(SynStack ss)  {    this.ss=ss;    }  public void run()  {    // push('a');//错误    try    {      Thread.sleep(2000);    }      catch(Exception e)    {    }    ss.push('a');      }}class Consumer implements Runnable{  private SynStack ss=null;  public Consumer(SynStack ss)  {    this.ss=ss;    }  public void run()  {    System.out.printf("%c\n",ss.pop());  }}public class TestPC{  public static void main(String[] args)  {    SynStack ss = new SynStack();    Producer p = new Producer(ss);    Consumer c = new Consumer(ss);      Thread t1=new Thread(p);    Thread t2=new Thread(c);    t1.start();    t2.start();  }    }
复制代码


PS:


  1. public void run()// throws Exception //错误 Runnable 里没有异常处理

  2. t2.start();//错误 因为无法判断是先进还是先出,如果先进则没错 但如果先出 则现在栈中没有元素 导致数组下标越界


结果:


  • 报数组溢出错误


解释:


  • 我们观察程序,发现我们创造了一个栈 有两个方法 分别是 pop()与 push() 出栈与入栈,这个栈其实就是 糖果池 而糖果池的最大容量就是 这个栈的最大值 6

  • 之后我们创建了生产者与消费者且他们都实现了 Runnable 接口与 run 方法,生产者会在 run 方法中 push()一个元素 也就是增加一个糖果,消费者会在 run 方法中 pop()一个元素 也就是减少一个糖果。

  • 最后我们创建了生产线程与消费线程,我们令生产线程的生产速率小于消费线程,最终会发现当糖果池为 0 后消费线程还在消费,导致溢出错误。

解决方案代码示例

class SynStack{  private char[] data = new char[6];  private int cnt = 0;      public synchronized void push(char ch)  {    while(cnt==data.length)    {      try      {        this.wait();        }        catch(Exception e)      {}    }    this.notify();    data[cnt]=ch;    ++cnt;    System.out.printf("生产线程正在生产第%d个产品,该产品是:%c\n",cnt,ch);    }      public synchronized char pop()  {    char ch;    if(cnt==0)    //注意:这里的注意同上    {      try      {        this.wait();        }        catch(Exception e)      {}    }    this.notify();    ch=data[cnt-1];    System.out.printf("消费线程正在消费第%d个产品,该产品是:%c\n",cnt,ch);      cnt--;    return ch;    }}

class Producer implements Runnable{ private SynStack ss=null; public Producer(SynStack ss) { this.ss=ss; } public void run()// throws Exception { // push('a');//错误 char ch; for(int i=0;i<20;i++) { try { Thread.sleep(200); } catch(Exception e) { } ch=(char)('a'+i); ss.push(ch); } }}

class Consumer implements Runnable{ private SynStack ss=null; public Consumer(SynStack ss) { this.ss=ss; } public void run() { for(int i=0;i<20;i++) { /*try { Thread.sleep(200); } catch(Exception e) { }*/ ss.pop(); } }}

public class TestPC_2{ public static void main(String[] args) { SynStack ss = new SynStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); Thread t1=new Thread(p); Thread t2=new Thread(c); t1.start(); t2.start(); } }
复制代码


PS:


  1. while(cnt==data.length)//注意://这里用 while 是为了保证 栈为满而暂停,然后下一次如果再满还是再这个循环里 保证再暂停,//如果改成 if 则只能暂停一次 下一次从暂停的那个位置继续走

  2. //但如果是同步过以后则可以用 if,因为如果满 则暂停(wait)生产线程 同时解除霸占,转换到//消费线程 进行消费操作 并锁住消费线程对象 导致一定会运行完消费操作 使得栈不为满 之后//唤醒生产操作 并结束消费程序 解锁,之后再竞争

  3. // 但这种 while 与 if 都可以用的情况 仅在 只有两个相互依赖的线程 的条件下成立,我们可以想一想 如果我们现在来一个毫不相干的线程 c c 运行完后也// 会唤醒某个线程 这样不满足条件的线程依旧可能被唤醒 并且因为是 if 所以直接跳出 if 进行生产操作 导致错误

  4. //使用 wait 会解锁,sleep 则不会

  5. this.notify();//唤醒其他某一个暂停的线程 对于这个例子 就是唤醒消费线程

  6. this.notify();//唤醒其他某一个暂停的线程 对于这个例子 就是唤醒生产线程

  7. public void run()// throws Exception //错误 Runnable 里没有异常处理

  8. Thread.sleep(200);//这种是生产比较慢 消费快

  9. Thread.sleep(200);//这种是消费比较慢 生产快


结果:


  • 生产线程正在生产第 1 个产品,该产品是:a

  • 消费线程正在消费第 1 个产品,该产品是:a

  • 生产线程正在生产第 1 个产品,该产品是:b

  • 消费线程正在消费第 1 个产品,该产品是:b

  • 生产线程正在生产第 1 个产品,该产品是:c

  • 消费线程正在消费第 1 个产品,该产品是:c

  • 生产线程正在生产第 1 个产品,该产品是:d

  • 消费线程正在消费第 1 个产品,该产品是:d

  • 生产线程正在生产第 1 个产品,该产品是:e

  • 消费线程正在消费第 1 个产品,该产品是:e

  • 生产线程正在生产第 1 个产品,该产品是:f

  • 消费线程正在消费第 1 个产品,该产品是:f

  • 生产线程正在生产第 1 个产品,该产品是:g

  • 消费线程正在消费第 1 个产品,该产品是:g

  • 生产线程正在生产第 1 个产品,该产品是:h

  • 消费线程正在消费第 1 个产品,该产品是:h

  • 生产线程正在生产第 1 个产品,该产品是:i

  • 消费线程正在消费第 1 个产品,该产品是:i

  • 生产线程正在生产第 1 个产品,该产品是:j

  • 消费线程正在消费第 1 个产品,该产品是:j

  • 生产线程正在生产第 1 个产品,该产品是:k

  • 消费线程正在消费第 1 个产品,该产品是:k

  • 生产线程正在生产第 1 个产品,该产品是:l

  • 消费线程正在消费第 1 个产品,该产品是:l

  • 生产线程正在生产第 1 个产品,该产品是:m

  • 消费线程正在消费第 1 个产品,该产品是:m

  • 生产线程正在生产第 1 个产品,该产品是:n

  • 消费线程正在消费第 1 个产品,该产品是:n

  • 生产线程正在生产第 1 个产品,该产品是:o

  • 消费线程正在消费第 1 个产品,该产品是:o

  • 生产线程正在生产第 1 个产品,该产品是:p

  • 消费线程正在消费第 1 个产品,该产品是:p

  • 生产线程正在生产第 1 个产品,该产品是:q

  • 消费线程正在消费第 1 个产品,该产品是:q

  • 生产线程正在生产第 1 个产品,该产品是:r

  • 消费线程正在消费第 1 个产品,该产品是:r

  • 生产线程正在生产第 1 个产品,该产品是:s

  • 消费线程正在消费第 1 个产品,该产品是:s

  • 生产线程正在生产第 1 个产品,该产品是:t

  • 消费线程正在消费第 1 个产品,该产品是:t


解释:


因为消费线程比较快,所以现在的情况就是生产一个立马就被消费了

发布于: 刚刚阅读数: 4
用户头像

Geek_65222d

关注

还未添加个人签名 2022-09-09 加入

还未添加个人简介

评论

发布
暂无评论
【Java深入学习】一个经典问题-消费者和生产者问题-下_十月月更_Geek_65222d_InfoQ写作社区