黑马程序员技术交流社区

标题: 一段不起眼的小程序引发的暴虐 [打印本页]

作者: 边情2015    时间: 2015-10-5 10:07
标题: 一段不起眼的小程序引发的暴虐
眼看下面这段不起眼的小程序,很简单,很单纯。似曾相识,却又不太一样。吃透它,细心体会其中的每一个细节。废话不多说。上代码!
记录每一天,先来一段插曲描述一下,程序后还有分析与思考:
用四个线程实现,两个生产者生产产品后,另两个消费者消费。要求生产一个产品必须先消费才能再生产下一个商品。
分析:生产者与消费者之前有共享资源(产品);且有多个线程(两个生产者与两个消费者)参与其中;生产者生产过程和消费过程都会使用到产品。以上三点,可以判断整个过程会引发安全隐患。我只需将双方使用产品的过程进行同步实现即可解决这个问题,这个安全隐患。
来吧,程序代码如下:
  1. class  ProduceConsumerDemo

  2. {

  3.          public static void main(String[] args)

  4.          {

  5.                    Resource r = new Resource();

  6.                    Thread t1 = new Thread(newProduce(r));

  7.                    Thread t2 = new Thread(newConsumer(r));

  8.                    Thread t3 = new Thread(newProduce(r));

  9.                    Thread t4 = new Thread(newConsumer(r));

  10.                   

  11.                    t1.start();

  12.                    t2.start();

  13.                    t3.start();

  14.                    t4.start();



  15.                    /*

  16.                    new Thread(newProduce(r)).start();

  17.                    new Thread(newConsumer(r)).start();

  18.                    new Thread(newProduce(r)).start();

  19.                    new Thread(newConsumer(r)).start();

  20.                    */

  21.          }

  22. }



  23. classResource

  24. {

  25.          private String name;

  26.          private int count = 1;

  27.          private boolean flag;

  28.          //t1和t2拥有不同的锁(Produce和Consumer对像),因此能够进入set函数内

  29.          public synchronized void set(Stringname)//同步函数的锁是this,在这里this代表它所在类Resource的引用(即调用都是对象)

  30.          {

  31.                    //if(flag)  //换while,那么问题就来了,程序会停住。这时,唤醒方式改成notifyAll就可以解决。                                                

  32.                                                         //考虑如何处理?想到反复判断flag不就解决了,因此用while(flag)替换if(flag)

  33.                                                //  t1放弃资格 t2获取资格//当t2获取资格后就不再判断flag,这样会出现还没消费掉t1生产的商品他t2就紧接着生产。

  34.                    while(flag)try{this.wait();}catch(Exception e){}

  35.                    this.name =name+"----"+count++;

  36.                    System.out.println(Thread.currentThread().getName()+"--生产者--"+this.name);

  37.                    flag = true;

  38.                    //this.notify();//notify唤醒的是池中的第一个等待中的线程 notifyAll唤醒池中所有等待中的进程

  39.                    this.notifyAll();

  40.          }



  41.          //t1和t2拥有不同的锁(Produce和Consumer对像),因此能够进入out函数内

  42.          public synchronized void out()//同步函数的锁是this,在这里this代表它所在类Resource的引用(即调用都是对象)

  43.          {

  44.                    /*if(!flag)*/

  45.                                                ////  t1放弃资格 t2获取资格

  46.                    while(!flag)       try{this.wait();}catch(Exception e){}

  47.                    System.out.println(Thread.currentThread().getName()+"--消费者--------"+name);

  48.                    flag = false;

  49.                    //this.notify();

  50.                    this.notifyAll();

  51.          }

  52. }



  53. classProduce implements Runnable

  54. {

  55.          Resource r = new Resource();

  56.          Produce(Resource r)

  57.          {

  58.                    this.r = r;

  59.          }

  60.          public void run()

  61.          {

  62.                    while(true)

  63.                    {

  64.                             r.set("商品");

  65.                    }

  66.          }

  67.         

  68. }

  69. classConsumer implements Runnable

  70. {

  71.          Resource r = new Resource();

  72.          Consumer(Resource r)

  73.          {

  74.                    this.r = r;

  75.          }

  76.          public void run()

  77.          {

  78.                    while(true)

  79.                    {

  80.                             r.out();

  81.                    }

  82.          }

  83.         

  84. }

复制代码

为实现同步,加入同步函数后发现。程序并达到没有预期的要求(产品必须先消费才能再生产下一个商品)。为达到这个目的,首先分析问题所在。经验证发现在使用产品过程中,同步函数监视器(锁)只能监视共同使用一个监视器的进程才有效,即无法监视具有不同监视器的对象。所以会导致程序中t1和t2对象判断语句判断后停留在那里,这样还会带来安全隐患。因此,为了达到多次判断,我们选择使用while循环语句。当然,引入while循环语句会带来程序停留的问题,不过使用Objec中的唤醒机制中的notifyAll完美解决了这个问。其中,一定要注意使用notify唤醒为什么会程序停留,因为其唤醒的只是池中的第一个进程。这样不难分析,其中生产者与消费者会多次使用产品的问题,而不是目标所要求的。


小小总结:要是有时间用思维导图体现,那就很Nice了。每天只24个小时,加油!



作者: Synaric    时间: 2015-10-5 11:02
不错的例子
作者: 冰霜之卅    时间: 2015-10-5 13:19
排版太差  不容易看懂。
作者: 往事如风555    时间: 2015-10-5 16:06
谢谢分享!
作者: blensmile    时间: 2015-10-5 18:36
谢谢分享~思维导图推荐imindmap~




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