------- 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.javapackage 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(); } } }
测试程序保持不变,运行结果:永久:自行车
劳斯莱斯:汽车
永久:自行车
劳斯莱斯:汽车
永久:自行车
劳斯莱斯:汽车
永久:自行车
劳斯莱斯:汽车
永久:自行车
劳斯莱斯:汽车