A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© itheima_llt 高级黑马   /  2015-4-11 22:37  /  355 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

生产者消费者问题

一、先处理只有1个生产者和1个消费者的情况

二、再处理2个生产者和2个消费者的情况

对于一、
步骤:
1 建立一个资源类,包含成员变量(商品名称,商品编号、监视器)
和方法(设置商品名称和商品编号)

2 建立生产者类,实现Runnable接口,覆盖run方法

3 建立消费者类,实现Runnable接口,覆盖run方法
  1. //1个消费者线程和1个生产者线程
  2. class Resource
  3. {
  4.         private String name;//商品名称
  5.         private int count=1;//商品编号
  6.         private boolean flag = false;//监视器
  7.        
  8.         //设置商品名称,编号自动加1
  9.         public synchronized void set(String name)
  10.         {
  11.                 if(flag)//如果资源监视器为真,那么调用本方法的对象等待
  12.                         try
  13.                         {
  14.                                 this.wait();
  15.                         }
  16.                         catch (Exception e)
  17.                         {
  18.                         }
  19.                                 this.name = name;
  20.                                  count++;
  21.                                  System.out.println(Thread.currentThread().getName()+"..."+name+"...生产者...编号:"+count);
  22.                                 flag = true;
  23.                                 this.notify();//唤醒线程池中的第一个线程
  24.         }

  25.         //获取商品名称和编号
  26.         public synchronized void get()
  27.         {
  28.                 if(!flag)//如果资源监视器为假,那么调用本方法的对象等待
  29.                 try
  30.                 {
  31.                         this.wait();
  32.                 }
  33.                 catch (Exception e)
  34.                 {
  35.                 }
  36.                         System.out.println(Thread.currentThread().getName()+"..."+name+"....消费者.........编号:"+count);
  37.                         flag = false;
  38.                         this.notify();//唤醒线程池中的第一线程
  39.         }

  40. }

  41. class Producer implements Runnable
  42. {
  43.         private Resource r;

  44.         //利用传递引用类型变量和构造函数避免造成初始化对象不唯一
  45.         Producer(Resource r)
  46.         {
  47.                 this.r =r;
  48.         }
  49.        
  50.         public void run()
  51.         {
  52.                 while(true)
  53.                         r.set("商品");
  54.         }

  55. }

  56. class Consumer implements Runnable
  57. {
  58.         private Resource r;

  59.         Consumer(Resource r)
  60.         {
  61.                 this.r = r;
  62.         }

  63.         public void run()
  64.         {
  65.                 while(true)
  66.                         r.get();
  67.         }
  68. }






  69. class MyProducerConsumerDemo
  70. {
  71.         public static void main(String[] args)
  72.         {
  73.                 //建立资源对象
  74.                 Resource r =new Resource();
  75.                
  76.                 //建立生产者对象
  77.                 Producer p =new Producer(r);

  78.                 //建立消费者对象
  79.                 Consumer c = new Consumer(r);

  80.                 //建立生产者线程
  81.                 Thread tp1 = new Thread(p);
  82.                
  83.                 //建立消费者线程
  84.                 Thread cp1 = new Thread(c);

  85.                 //启动生产者线程
  86.                 tp1.start();

  87.                 //启动消费者线程
  88.                 cp1.start();
  89.                
  90.                
  91.                 //简化代码
  92.                 //new Thread(new Producer(r)).start();
  93.                 //new Thread(new Consumer(r)).start();
  94.                
  95.         }
  96. }
复制代码
  1. //两个生产者和两个消费者线程
  2. class Resource
  3. {
  4.         private String name;//商品名称
  5.         private int count=1;//商品编号
  6.         private boolean flag = false;//监视器
  7.        
  8.         //设置商品名称,编号自动加1
  9.         public synchronized void set(String name)
  10.         {
  11.                 //if(flag)//如果资源监视器为真,那么调用本方法的对象等待
  12.                 while(flag)
  13.                         try
  14.                         {
  15.                                 this.wait();
  16.                         }
  17.                         catch (Exception e)
  18.                         {
  19.                         }
  20.                                 this.name = name;
  21.                                  count++;
  22.                                  System.out.println(Thread.currentThread().getName()+"..."+name+"...生产者...编号:"+count);
  23.                                 flag = true;
  24.                                 this.notifyAll();//唤醒线程池中的第一个线程
  25.         }

  26.         //获取商品名称和编号
  27.         public synchronized void get()
  28.         {
  29.                
  30.                 //if(!flag)//如果资源监视器为假,那么调用本方法的对象等待
  31.                 while(!flag)
  32.                 try
  33.                 {
  34.                         this.wait();
  35.                 }
  36.                 catch (Exception e)
  37.                 {
  38.                 }
  39.                         System.out.println(Thread.currentThread().getName()+"..."+name+"....消费者.........编号:"+count);
  40.                         flag = false;
  41.                         this.notifyAll();//唤醒线程池中的第一线程
  42.         }

  43. }

  44. class Producer implements Runnable
  45. {
  46.         private Resource r;

  47.         //利用传递引用类型变量和构造函数避免造成初始化对象不唯一
  48.         Producer(Resource r)
  49.         {
  50.                 this.r =r;
  51.         }
  52.        
  53.         public void run()
  54.         {
  55.                 while(true)
  56.                         r.set("商品");
  57.         }

  58. }

  59. class Consumer implements Runnable
  60. {
  61.         private Resource r;

  62.         Consumer(Resource r)
  63.         {
  64.                 this.r = r;
  65.         }

  66.         public void run()
  67.         {
  68.                 while(true)
  69.                         r.get();
  70.         }
  71. }






  72. class MyProducerConsumerDemo
  73. {
  74.         public static void main(String[] args)
  75.         {
  76.                 //建立资源对象
  77.                 Resource r =new Resource();

  78.                 //建立生产者对象
  79.                 Producer p =new Producer(r);

  80.                 //建立消费者对象
  81.                 Consumer c = new Consumer(r);

  82.                 //建立生产者线程
  83.                 Thread tp1 = new Thread(p);
  84.                 Thread tp2 = new Thread(p);
  85.                
  86.                 //建立消费者线程
  87.                 Thread cp1 = new Thread(c);
  88.                 Thread cp2 = new Thread(c);

  89.                 //启动生产者线程
  90.                 tp1.start();
  91.                 tp2.start();

  92.                 //启动消费者线程
  93.                 cp1.start();
  94.                 cp2.start();
  95.         }
  96. }
复制代码

运行结果出现:
生产一次,消费2次的情况。
Thread-1...商品...生产者...编号:359
Thread-3...商品....消费者.........编号:359
Thread-2...商品....消费者.........编号:359

生产2次,消费1次的情况。
Thread-1...商品...生产者...编号:3424
Thread-0...商品...生产者...编号:3425
Thread-3...商品....消费者.........编号:3425


这说明,原来在1个生产者和1个消费者安全
但在2个生产者和2个消费者就不安全了。

为什么会这样呢??
因为在用if语句判断的时候,当线程被唤醒后,它并没有再判断flag,
而是直接执行下一条语句,所以我们可以把if改成while。
> if(flag)//如果资源监视器为真,那么调用本方法的对象等待

改成while(flag)的时候,运行结果为:
Thread-0...商品...生产者...编号:2
Thread-3...商品....消费者.........编号:2
Thread-1...商品...生产者...编号:3
Thread-3...商品....消费者.........编号:3
Thread-0...商品...生产者...编号:4

4个线程全部处于睡眠状态,不再打印。

这又是因为睡眠呢??
原因是notify唤醒的都是线程池中的第一个线程,
这就导致了被唤醒的线程不一定是对方的线程。
而当唤醒的是本方线程时候,线程就全部陷入了等待。
该怎么解决呢??
可以用notifyAll();

运行结果显示安全问题被解决!

小结:
在多个生产者和多个消费者的情况下,

用while+notifyAll();

1 个回复

倒序浏览
{:3_54:}自己顶一下!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马