zoukankan      html  css  js  c++  java
  • 生产者消费者问题

    生产者和消费者问题是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 }
  • 相关阅读:
    使用QOAuth来进行新浪/腾讯微博验证(二)
    很不错的Utility库,C#4扩展 各种功能齐全,两行代码搞定图片转字符
    使用QOAuth来进行新浪/腾讯微博验证(一)
    可怜的小猪&香农熵
    消息队列MQ如何保证消息不丢失
    40 亿个 QQ 号码如何去重,bitmap去重
    参数的设置
    自动化测试的十个要点
    LR学习中的一个低级错误
    Windows下用CMake编译libuv
  • 原文地址:https://www.cnblogs.com/wsw-tcsygrwfqd/p/6395377.html
Copyright © 2011-2022 走看看