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

------- 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培训、期待与您交流! ----------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值