您的位置:首页 > 职场人生

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

2013-03-12 21:39 651 查看
-------
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培训、期待与您交流! ----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JAVA 多线程