Java中线程的学习(五)——生产者消费者的两种实现

生产者消费者模型应该是计算机经常涉及到的,我在上计算机组成原理、操作系统课时,都有讲到过,而此模型在编程中也是会经常涉及到。生产者负责生产数据,消费者负责消耗数据,如果我们直接让消费者去调用生产者里面的方法去消耗数据的话,要是某一天,消费者的代码发生变化,生产者可能也会受到影响。简单来说,生产者消费者之间应该是通过一个中间缓冲区去相互使用,这样它们之间的依赖关系就没有那么强烈,也达到了松耦合的目的。

现在有个需求,生产者生产手机,消费者消费手机,生产两种类型的手机,且要交替生产,下面看看代码:

/**
 * 建立手机实体类,设置属性有品牌、价格
 * @author littledyf
 *
 */
public class Phone {
	
	private String brand;
	private double price;
	/*设置库存,如果没有,store为false,如果有store为true;
	 * 没有的时候,由生产者生产,消费者不能消费;
	 * 有的时候,由消费者消费。生产者不能生产。
	 */
	private boolean store = false;
	public boolean isStore() {
		return store;
	}

	public void setStore(boolean store) {
		this.store = store;
	}

	public Phone() {
	}

	public Phone(String brand, double price) {
		this.brand = brand;
		this.price = price;
	}

	public String getBrand() {
		return brand;
	}

	public void setBrand(String brand) {
		this.brand = brand;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Phone [brand=" + brand + ", price=" + price + "]";
	}

}
public class Producer extends Thread{
	private Phone phone;
	public Producer(Phone phone) {
		// TODO Auto-generated constructor stub
		this.phone = phone;
	}


	@Override
	public void run() {
		// TODO Auto-generated method stub
		boolean bool = true;
		while(true){
			//可以直接用phone作为锁对象,因为phone只有一个对象
			synchronized (phone) {
				//当有库存的时候,即store为true的时候,等待
				if(phone.isStore() == true){
					try {
						phone.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				//如果bool为true,生产HTC,否则生产中兴
				if(bool == true){
					phone.setBrand("HTC");
					phone.setPrice(200);
				}else{
					phone.setBrand("中兴");
					phone.setPrice(100);
				}
				//每生产一次就置反,为了让另外一个生产
				bool = !bool;
				//生产了之后将库存置为true,这时不能生产,只能消费
				phone.setStore(true);
				//当生产了之后,唤醒消费者线程
				phone.notify();
			}
			
			
		}
	}
}
public class Consumer extends Thread{
	private Phone phone;
	public Consumer(Phone phone) {
		// TODO Auto-generated constructor stub
		this.phone = phone;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized (phone) {
				//如果没有库存,此线程等待
				if(phone.isStore() == false){
					try {
						phone.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println(phone.getBrand() + "---" + phone.getPrice());
				//消费了之后,将库存设置为false
				phone.setStore(false);
				//消费了之后,唤醒生产线程,这里的phone为监视器
				phone.notify();
			}
			
		}
	}
}
/**
 * 生产者消费者测试
 * 需求:生产者生产两种型号的手机,且要求交替生产,消费者去进行消费
 * @author littledyf
 *
 */
public class TestPC {
	public static void main(String[] args) {
		//创建实体类对象
		Phone phone = new Phone();
		//创建任务,多个线程执行多个任务,但任务又是相关联的
		Producer p = new Producer(phone);
		Consumer c = new Consumer(phone);
		p.start();
		c.start();
	}
}

 

分析:看到需求之后,我们先分析从哪里下手,竟然是一个生产手机,一个消费手机,那么,是不是需要先生产才能消费?要生产手机,就应该先有手机这个类,那我们先创建手机类。创建好手机类后,再去创建生产者,生产者继承Thread,生产是一个线程嘛。生产手机,那么久把手机类phone传入生产者,生产者重写run方法。这里直接while(true)不停生产,我们需要在没有库存的时候再去生产,所以,我们需要在手机类里设置库存,刚开始store为false,就是当store为false时才生产,true的时候不生产,开始等待消费者消费。然后生产手机,需要交替生产,那么我们直接用一个boolean值来进行交替,当bool为true的时候生产一种机型,生产了之后bool置反,当bool为false生产另外一种,生产完又置反。这时生产好了,我们就需要唤醒消费者线程。值得注意的是,为了不让两者争抢资源发生不好的事情,我们需要加锁,而锁对象直接可以使用phone对象。我们不是让在有库存和没库存的时候去等待另外一个线程运行嘛,所以,我们需要等待和唤醒,而等待和唤醒我们需要一个监视器,而监视器用哪个对象?简单一点,锁对象是谁,就用谁作为监视器。而关于监视器,可以再去看看相关的文章,我们这里主要讲解生产者消费者。其实在写代码的时候,我们不需要一次性把一个类写完,而是分析先需要写哪个,写完再写下一个,然后看下一个需要什么,我们再去写需要的东西,给我的感觉就像是一个切面的感觉。就是很多类或者对象之间不是垂直的关系,而是一种面向切面的那种关系。

接下来我们看看使用仓储来实现生产者消费者案例:

**
 * 生产者消费者仓储模型
 * @author littledyf
 *
 */
public class Store {
	private int max = 20;//最大存储量
	private int sum = 0;//产品数量
	//入库
	public void push() {
		// TODO Auto-generated method stub
		synchronized (this) {
			//如果产品数量大于等于最大存储量,等待
			if(sum >= max){
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			//商品入库之后加1
			sum++;
			System.out.println("商品入库:" + sum);
			//唤醒出库
			this.notify();
		}
		
	}
	//出库
	public void pop() {
		// TODO Auto-generated method stub
		synchronized (this) {
			//如果商品存量为0,等待
			if(sum == 0){
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			//商品出库减1
			sum--;
			System.out.println("商品出库,剩余商品:" + sum);
			//唤醒入库
			this.notify();
		}
		
	}
}
public class Producer extends Thread{
	private Store store;
	public Producer(Store store) {
		this.store = store;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			//生产者调用仓储模型里的入库
			store.push();
		}
	}
}
public class Consumer extends Thread{
	private Store store;
	public Consumer(Store store) {
		this.store = store;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			//消费者调用仓储模型里的出库
			store.pop();
		}
	}

}
public class TestPCS {
	public static void main(String[] args) {
		
		//建立一个仓储模型的对象
		Store store = new Store();
		
		//让生产者和消费者分别去仓储模型里调用该使用的方法
		Producer p = new Producer(store);
		Consumer c = new Consumer(store);
		p.start();
		c.start();
	}
}

生产者消费者的仓储模型,其实就是生产者消费者不做具体的方法,具体方法放在一个仓库,即store里面去实现,生产者消费者只需要去调用仓库里面的方法即可。

之后在编程的时候,只要遇到多个线程去执行多个任务,而多个任务又是相互关联的,我们就可以使用生产者消费者模型直接去套。

猜你喜欢

转载自blog.csdn.net/qq_41061437/article/details/82048346
今日推荐