生产者消费者问题

来源:互联网 发布:node.js能实现什么 编辑:程序博客网 时间:2024/05/22 03:43
生产者和消费者问题是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 Runnable103 {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  ProAndConLockSolution119 {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 }

 

0 0
原创粉丝点击