生产者消费者问题

生产者和消费者问题是java多线程中的等待唤醒机制的经典案例。

下面通过几个例子,循序渐进得了解这个问题。

1.单生产者和单消费者

  1 class Res
  2 {
  3     private String name;
  4     private int count=0;
  5     private boolean flag=false;//用来标志资源是否为空
  6     public Res(String name)
  7     {
  8         this.name=name;
  9     }
 10     //为其他类提供实现资源存放的方法
 11     public synchronized void  set()
 12     {
 13         //如果资源内容是满的
 14         //此时执行的线程,即生产者线程等待
 15         if(flag)
 16         {    try
 17             {
 18                 this.wait();
 19             }
 20             catch (Exception e)
 21             {
 22                 System.out.println("出错了");
 23             }
 24         }
 25         //否则,资源内容为空
 26         //新生产一个产品
 27         count++;
 28         System.out.println("生产者生产了"+name+count);
 29         //修改标志
 30         flag=true;
 31         //唤醒在此对象监视器上等待的某个线程,即消费者线程
 32         //第一次执行时,没有等待线程,为空唤醒
 33         this.notify();
 34     }
 35 
 36     //为其他类提供资源提取的方法
 37     public synchronized void  out()
 38     {
 39         //如果资源内容是空的
 40         //此时执行的线程,即消费者线程等待
 41         if(!flag)
 42         {    try
 43             {
 44                 this.wait();
 45             }
 46             catch (Exception e)
 47             {
 48                 System.out.println("出错了");
 49             }
 50         }
 51         //否则,资源内容存在
 52         //则消费该产品
 53         System.out.println("消费者消费了~~~~~~~~~"+name+count);
 54         //修改标志
 55         flag=false;
 56         //唤醒在此对象监视器上等待的某个线程,即生产者线程
 57         this.notify();
 58     }
 59 }
 60 class Producer1 implements Runnable
 61 {
 62     Res r;
 63     public Producer1(Res r)
 64     {
 65         this.r=r;
 66     }
 67 
 68 //实现run方法
 69     public void run()
 70     {
 71     
 72         while(true)
 73         {
 74             r.set();
 75         }
 76     }
 77 
 78 }
 79 class Customer1 implements Runnable
 80 {
 81     Res r;
 82     public Customer1(Res r)
 83     {
 84         this.r=r;
 85     }
 86 
 87     //实现run方法
 88     public void run()
 89     {
 90         while(true)
 91         {
 92             r.out();
 93         }
 94     }
 95 }
 96 public  class  ProducerAndCustomerDemo1
 97 {
 98     public static void main(String[] args) 
 99     {
100         //建立资源对象
101         Res r=new Res("冰淇淋");
102         //创建生产者
103         Producer1 pro=new Producer1(r);
104         //创建消费者
105         Customer1 con=new Customer1(r);
106 
107         //建立线程
108         Thread t1=new Thread(pro);
109         Thread t2=new Thread(con);
110         //开启线程
111         t1.start();
112         t2.start();
113 
114     }
115 }

运行如下:

2.多生产者和多消费者问题

多生产者和多消费者的情况,如果仍然按照上述写法来写,会发生两种错误:

第一种,输出表现是某个产品被消费了多次或者某个产品没有被消费。

产生原因是,线程被唤醒后接着往下执行,没有再判断flag标记。

解决办法:把if语句,改为while循环。

这时,又会产生第二种错误,死锁。

输出表现:程序自行停止。

产生原因:线程进行唤醒动作时,唤醒了本类线程(例如,消费者线程唤醒了消费者线程),

没能达到唤醒对方的效果(例如,消费者线程应该唤醒生产者线程),而本类线程在进行标志位判断后

,也进入了等待状态,最终致使所有线程都在等待状态,程序停止。

解决办法:把notify语句改为notifyAll,确保线程每次执行唤醒动作时,都能达到唤醒对方的效果。

  1 class Res
  2 {
  3     private String name;
  4     private int count=0;
  5     private boolean flag=false;
  6     public Res(String name)
  7     {
  8         this.name=name;
  9     }
 10     
 11     public synchronized void  set()
 12     {
 13         //while循环判断标志位
 14         while(flag)
 15         {    try
 16             {
 17                 this.wait();
 18             }
 19             catch (Exception e)
 20             {
 21                 System.out.println("出错了");
 22             }
 23         }
 24         
 25         count++;
 26         System.out.println("生产者"+Thread.currentThread().getName()+"生产了"+name+count);
 27         flag=true;
 28         //唤醒在此对象监视器上等待的所有线程
 29         this.notifyAll();
 30     }
 31 
 32     public synchronized void  out()
 33     {
 34         //while循环判断标志位
 35         while(!flag)
 36         {    try
 37             {
 38                 this.wait();
 39             }
 40             catch (Exception e)
 41             {
 42                 System.out.println("出错了");
 43             }
 44         }
 45         System.out.println("消费者"+Thread.currentThread().getName()+"消费了~~~~~~~~~"+name+count);
 46         flag=false;
 47         //唤醒在此对象监视器上等待的所有线程
 48         this.notifyAll();
 49     }
 50 }
 51 class Producer1 implements Runnable
 52 {
 53     Res r;
 54     public Producer1(Res r)
 55     {
 56         this.r=r;
 57     }
 58 
 59     public void run()
 60     {
 61     
 62         while(true)
 63         {
 64             r.set();
 65         }
 66     }
 67 
 68 }
 69 class Customer1 implements Runnable
 70 {
 71     Res r;
 72     public Customer1(Res r)
 73     {
 74         this.r=r;
 75     }
 76 
 77     public void run()
 78     {
 79         while(true)
 80         {
 81             r.out();
 82         }
 83     }
 84 }
 85 public  class  ProducerAndCustomerDemo2
 86 {
 87     public static void main(String[] args) 
 88     {
 89         //建立资源对象
 90         Res r=new Res("冰淇淋");
 91         //创建生产者
 92         Producer1 pro=new Producer1(r);
 93         //创建消费者
 94         Customer1 con=new Customer1(r);
 95 
 96         //建立线程
 97         Thread t0=new Thread(pro);
 98         Thread t1=new Thread(pro);
 99         Thread t2=new Thread(con);
100         Thread t3=new Thread(con);
101         //开启线程
102         t0.start();
103         t1.start();
104         t2.start();
105         t3.start();
106 
107     }
108 }

运行如下:

3.多生产者和多消费者问题(JDK1.5的新特性)

两个代替:

JDK1.5的新特性
用Lock接口的实现类,封装锁对象,显式完成申请锁和释放锁的动作,以此代替synchronized同步方法的使用;
Lock实现类的锁对象,最大的好处是,一个锁可以有多个监视器(即Condition对象,而同步代码块的锁只能有一个监视器),
这样就可以分别为生产者和消费者建立不同的监视器,实现只唤醒对方的操作,用指定监视器的singal方法代替了notifyAll方法。

  1 import java.util.concurrent.locks.*;
  2 class Rec
  3 {
  4     private String name;
  5     private int count=0;
  6     private boolean flag=false;
  7     //建立锁对象
  8     Lock lock=new ReentrantLock();
  9     //得到该锁对象的两个监视器
 10     //分别用于生产者和消费者
 11     Condition producer_con=lock.newCondition();
 12     Condition customer_con=lock.newCondition();
 13 
 14     public Rec(String name)
 15     {
 16         this.name=name;
 17     }
 18     public void set()
 19     {
 20         //获取锁
 21         lock.lock();
 22         try
 23         {
 24             while(flag)
 25             try
 26             {
 27                 //调用Condition对象的await方法
 28                 producer_con.await();
 29             }
 30             catch (Exception e)
 31             {
 32                 System.out.println("有错误");
 33             }
 34             count++;
 35             System.out.println(Thread.currentThread().getName()+"生产者~~JDK1.5~~,生产出了"+name+count);
 36             
 37             flag=true;
 38             //用消费者线程的监视器唤醒消费者
 39             customer_con.signal();
 40         }
 41         /*加入try finally结构
 42         原因:如果try代码块中出现异常,释放锁的动作就不会被执行
 43         因此把释放锁动作放在finally中,确保其执行。
 44         
 45         */
 46         finally
 47         {
 48             //释放锁
 49             lock.unlock();
 50         }
 51     }
 52 
 53     public void out()
 54     {
 55         //获取锁
 56         lock.lock();
 57         try
 58         {
 59             while(!flag)
 60             try
 61             {
 62                 customer_con.await();
 63             }
 64             catch (Exception e)
 65             {
 66                     System.out.println("有错误");
 67 
 68             }
 69 
 70         System.out.println(Thread.currentThread().getName()+"消费者~~~~~~~~~JDK1.5~~~~~~~~~~吃掉了"+name+count);
 71         flag=false;
 72         //用生产者线程的监视器唤醒生产者
 73         producer_con.signal();
 74         }
 75         finally
 76         {
 77             //释放锁
 78             lock.unlock();
 79         }
 80         
 81             
 82     }
 83 
 84 
 85 }
 86 class Producer implements Runnable
 87 {
 88     Rec r;
 89     public Producer(Rec r)
 90     {
 91         this.r=r;
 92     }
 93 
 94     public void run()
 95     {
 96         while(true)
 97         {
 98             r.set();
 99         }
100     }
101 }
102 class Customer implements Runnable
103 {
104     Rec r;
105     public Customer(Rec r)
106     {
107         this.r=r;
108     }
109 
110     public void run()
111     {
112         while(true)
113         {
114             r.out();
115         }
116     }
117 }
118 public class  ProAndConLockSolution
119 {
120     public static void main(String[] args) 
121     {
122         Rec r=new Rec("北京烤鸭");
123         Producer pro1=new Producer(r);
124         Producer pro2=new Producer(r);
125         Customer con1=new Customer(r);
126         Customer con2=new Customer(r);
127 
128         Thread t0=new Thread(pro1);
129         Thread t1=new Thread(pro2);
130         Thread t2=new Thread(con1);
131         Thread t3=new Thread(con2);
132 
133         t0.start();
134         t1.start();
135         t2.start();
136         t3.start();
137 
138 
139     }
140 }
原文地址:https://www.cnblogs.com/wsw-tcsygrwfqd/p/6395377.html