黑马程序员——多线程操作经典实例:生产者消费者问题

来源:互联网 发布:淘宝客如意投 编辑:程序博客网 时间:2024/05/22 05:21

------- android培训、java培训、期待与您交流! ----------


        生产者消费者问题是线程操作中的一个经典案例,该问题最早由Dijkstra提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。看了买的教材中对生产者消费者问题的讲解非常的精彩,于是记下此篇日志加深理解并且便于以后复习。

        根据需求,我们开始一步步的分析实现:

首先,程序中生产者和消费者都有一个操作的对象,也就是产品,于是我们先定义一个包含产品信息的Product类:

Product.java

package com.itheima;public class Product{private String name;// 定义name属性private String category;// 定义category属性public void setName(String name){this.name = name;}public void setCategory(String category){this.category = category;}public String getName(){return this.name;}public String getCategory(){return this.category;}}

因为生产者和消费者操作的是同一个空间的内容,所以我们让生产者和消费者分别实现Runnable接口以接收Product类的实例。Producer和Consumer类的定义如下:

Producer.java

package com.itheima;class Producer implements Runnable{private Product product;public Producer(Product product){this.product = product;}public void run(){boolean flag = false;for(int i=0;i<10;i++){if(flag){this.product.setName("永久");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.product.setCategory("自行车");flag = false;}else{this.product.setName("劳斯莱斯");try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.product.setCategory("汽车");flag = true;}}}}

Consumer.java
package com.itheima;class Consumer implements Runnable{private Product product;public Consumer(Product product){this.product = product;}public void run(){for(int i=0;i<10;i++){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace() ;}System.out.println(this.product.getName()+            ":"+this.product.getCategory());}}}

然后我们编写测试程序对程序进行检测
package com.itheima;public class Test{public static void main(String args[]){Product product = new Product();Producer pro = new Producer(product);Consumer con = new Consumer(product);new Thread(pro).start();new Thread(con).start();}}

        运行结果:

        永久:汽车
        劳斯莱斯:自行车
        永久:汽车
        劳斯莱斯:自行车
        永久:汽车
        劳斯莱斯:自行车
        永久:汽车
        劳斯莱斯:自行车
        永久:汽车
        永久:自行车

        我们发现乱套了,不仅不像我们开始定义的“永久:自行车”和“劳斯莱斯:汽车”,而且还有重复两次输出“永久”的情况。这是为什么呢?因为生产者和消费者的线程都已启动,这样不能保证谁在前,或者谁在后,在生产者还在设置内容的时候,消费者已经取走了内容,那么显示的肯定就会出现不匹配的结果。而且这两个方法在不同的线程中,当一条线程中的方法设置完内容之后,另一个线程取出内容显示,取完之后继续取,这样生产者无法更新内容,那显示出来的内容就会出现重复。

        这个时候,我们就应该定义一个boolean型标记,并使用wait()和notify()方法用来控制他们轮流对资源进行操作,避免因为资源分配不均衡导致的重复输出错误;用synchronized关键字来声明set()和get()方法,把他们定义成一个同步方法,这样便可以避免输出不匹配的结果。改过之后代码如下:

Product.java

package com.itheima;class Product{private String name;private String category;private boolean flag = false;public synchronized void set(String name,String category){if(!flag){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}this.setName(name);try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}this.setCategory(category) ;flag  = false;//改变标志位,表示可以取走super.notify();}public synchronized void get(){if(flag){try{super.wait();}catch(InterruptedException e){e.printStackTrace();}}try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(this.getName()+":"+this.getCategory());flag  = true;//改变标志位,表示可以生产super.notify();}public void setName(String name){this.name = name;}public void setCategory(String category){this.category = category;}public String getName(){return this.name;}public String getCategory(){return this.category;}}

Producer.java

package com.itheima;class Producer implements Runnable{private Product product;public Producer(Product product){this.product = product;}public void run(){boolean flag = false;for(int i=0;i<10;i++){if(flag){this.product.set("永久","自行车");flag = false;}else{this.product.set("劳斯莱斯","汽车");flag = true;}}}}

Consumer.java

package com.itheima;class Consumer implements Runnable{private Product product;public Consumer(Product product){this.product = product;}public void run(){for(int i=0;i<10;i++){this.product.get();}}}

        测试程序保持不变,运行结果:

        永久:自行车
        劳斯莱斯:汽车
        永久:自行车
        劳斯莱斯:汽车
        永久:自行车
        劳斯莱斯:汽车
        永久:自行车
        劳斯莱斯:汽车
        永久:自行车
        劳斯莱斯:汽车


------- android培训、java培训、期待与您交流! ----------

原创粉丝点击