黑马程序员技术交流社区

标题: 关于多线程中监视器方法的问题 [打印本页]

作者: haozi050    时间: 2014-2-1 21:05
标题: 关于多线程中监视器方法的问题
本帖最后由 haozi050 于 2014-2-9 08:35 编辑

在多生产者多消费者例子中,如果只创建一个监视器方法对象的话(如代码一),结果是只生产了一个馒头还没有消费,而且死锁了。我猜想是这个监视器让所有线程处于等待状态了。如果创建两个监视器对象的话,问题就解决了(如代码二)。这时调用await()方法为什么没死锁呢? 问题是监视器对象它怎么知道是一个监视器对应生产者,一个对应消费者呢?这中间的到底是怎么监视的?请高手回答详细点,谢了!
代码一:
  1. import java.util.concurrent.locks.*;
  2. class Resource
  3. {
  4. //        定义商品名称和编号
  5.         private String name;
  6.         private int count=1;
  7. //        添加一个标记,如果为假则生产否则不生产只消费。
  8.         boolean  flag=false;
  9. //        为方便起见,将生产馒头方式和消费馒头方式定义在这里
  10.         Lock lock=new ReentrantLock();
  11. //        根据锁获取监视器方法对象
  12.         Condition c=lock.newCondition();
  13.         public  void set(String name)
  14.         {
  15.                 try{
  16.                         lock.lock();
  17.                         while(flag)
  18.                         {
  19.                                 try{c.await();}catch(InterruptedException e){}
  20. //                                这个程序的结果是只生产了一个馒头还没有消费,而且死锁了。可以推断出这个监视器让所有线程处于等待状态了。所以要想只让本方
  21. //                                等待的话,要创建两个监视器对象。
  22.                         }
  23.                                                 this.name=name+"--"+count;
  24.                                                 System.out.println(Thread.currentThread().getName()+"...生产者..."+name+count);
  25.                                                 count++;
  26.                                                 flag=true;
  27.                                                 c.signalAll();
  28.                         }finally{
  29.                                 lock.unlock();
  30.                 }
  31.                
  32.         }
  33.         public synchronized void get()
  34.         {
  35. //                对于消费者如果为假的话表示没有馒头需要等待,如果为真的话就消费,然后在将标记改为假,唤醒生产者,自己再等待。
  36.                 try{
  37.                                 lock.lock();
  38.                         while(!flag)
  39.                         {
  40.                                         try{c.await();}catch(InterruptedException e){}
  41.                         
  42.                                                 System.out.println(Thread.currentThread().getName()+"...消费者..."+name);
  43.                                                 flag=false;
  44.                                                 c.signalAll();
  45.                                        
  46.                         }
  47.                 }
  48.                 finally{
  49.                         lock.unlock();
  50.                 }
  51.                
  52. }
  53. }
  54. class Producer1 implements Runnable
  55. {
  56.         Resource r;
  57.         Producer1(Resource r)
  58.         {
  59.                 this.r=r;
  60.         }
  61.         public void run()
  62.         {
  63.                 while(true)
  64.                 {
  65.                         r.set("馒头");
  66.                 }
  67.         }
  68. }

  69. class Consumer1 implements Runnable
  70. {
  71.         Resource r;
  72.         Consumer1(Resource r)
  73.         {
  74.                 this.r=r;
  75.         }
  76.         public void run()
  77.         {
  78.                 while(true)
  79.                 {
  80.                         r.get();
  81.                 }
  82.         }
  83. }
  84. class ProductorConsumerDemo2
  85. {

  86.         public static void  main(String []args)
  87.         {
  88.                 Resource r=new Resource();
  89.                 Producer1 p=new Producer1(r);//创建生产任务
  90.                 Consumer1 c=new Consumer1(r);//创建消费任务
  91.                 Thread t0=new Thread(p);//创建线程明确生产任务
  92.                 Thread t1=new Thread(p);//创建线程明确生产任务
  93.                 Thread t2=new Thread(c);//创建线程明确消费任务
  94.                 Thread t3=new Thread(c);//创建线程明确生产任务
  95.                 t0.start();
  96.                 t1.start();
  97.                 t2.start();
  98.                 t3.start();
  99.         }

  100. }
复制代码

代码二:

  1. import java.util.concurrent.locks.*;
  2. //定义资源类
  3. class OneResource
  4. {
  5.         private String name;//名称
  6.         private int count=1;//编号
  7.         boolean flag=false;//标记
  8. //        创建一个锁对象
  9.         Lock lock=new ReentrantLock();
  10. //        创建两个监视器方法对象,一个监视生产者,一个监视消费者
  11.         Condition produce=lock.newCondition();
  12.         Condition consume=lock.newCondition();
  13.         public void set(String name)//定义生产馒头方法
  14.         {
  15. //                上锁
  16.                 lock.lock();
  17. //                如果标记为假就进行生产否则就等待。由于会抛异常,防止异常之后没释放锁,要用try finally
  18.                 try{
  19.                         while(flag)
  20.                         {try{produce.await();}catch(InterruptedException e){}}
  21.                         this.name=name+"--"+count;
  22.                         System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
  23.                         count++;
  24.         //                生产完之后将标记改为真,唤醒消费者线程。
  25.                         flag=true;
  26.                         consume.signal();
  27.                 }
  28. //                释放锁
  29.                 finally{
  30.                         lock.unlock();
  31.                 }
  32.         }
  33.         public void get()//定义消费方法
  34.         {
  35.                 lock.lock();
  36.                 try{
  37.         //                如果标记为真则消费,否则等待
  38.                         while(!flag)
  39.                         {
  40.                                 {try{consume.await();}catch(InterruptedException e){}}
  41.                         }
  42.                         System.out.println(Thread.currentThread().getName()+"..消费者.."+this.name);
  43.         //                 消费完之后将标记改为假,唤醒生产者
  44.                         flag=false;
  45.                         produce.signal();
  46.                 }finally{
  47.                         lock.unlock();
  48.                 }
  49.          }
  50. }
  51. //定义生产者
  52. class MoreProducer implements Runnable
  53. {
  54. //        为了保证资源一致,这里定义构造函数接受资源对象,一初始化时就有资源对象。
  55.         OneResource r;
  56.         MoreProducer(OneResource r)
  57.         {
  58.                 this.r=r;
  59.         }
  60. //        明确生产者的任务,生产馒头
  61.         public void run()
  62.         {
  63. //                一直生产
  64.                 while(true)
  65.                 r.set("馒头");
  66.         }
  67. }
  68. //定义消费者类
  69. class MoreConsumer implements  Runnable
  70. {
  71. //        一初始化也要有统一的资源对象
  72.         OneResource r;
  73.         MoreConsumer(OneResource r)
  74.         {
  75.                 this.r=r;
  76.         }
  77. //        明确消费者的消费任务
  78.         public void run()
  79.         {
  80. //                一直消费
  81.                 while(true)
  82.                         r.get();
  83.         }
  84. }
  85. public class ProducerConsumerFinal {

  86.         public static void main(String[] args) {
  87. //创建资源对象,并将资源传递给生产者消费者
  88. //                创建任务对象即生产任务和消费任务,生产任务和消费任务都只有一个
  89.                 OneResource  r=new OneResource();
  90.                 MoreProducer p1=new MoreProducer(r);
  91.                 MoreConsumer c1=new MoreConsumer(r);
  92. //                创建线程并指定线程任务
  93.                 Thread t0=new Thread(p1);
  94.                 Thread t1=new Thread(p1);
  95.                 Thread t2=new Thread(c1);
  96.                 Thread t3=new Thread(c1);
  97.                 t0.start();
  98.                 t1.start();
  99.                 t2.start();
  100.                 t3.start();
  101.                
  102.         }

  103. }
复制代码






作者: e.c    时间: 2014-2-4 15:41
本帖最后由 e.c 于 2014-2-4 15:44 编辑

代码1是楼主马虎的原因~
消费者:get 里的while循环结束的地方错了
还有get不要加synchronized
作者: e.c    时间: 2014-2-4 15:45
e.c 发表于 2014-2-4 15:41
代码1是楼主马虎的原因~
消费者:get 里的while循环结束的地方错了
还有get不要加synchronized ...

while(!flag)
                        {
                                        try{c.await();}catch(InterruptedException e){}
                                                System.out.println(Thread.currentThread().getName()+"...消费者..."+name);
                                                flag=false;
                                                c.signalAll();
                        }      //循环结束应该放在try{}catch(){} 后面




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2