生产者-消费者模式V1

Preview:


       1. 针对虚假唤醒问题,使用while重复检测临界值(即,wait()方法的调用永远要放到while循环中)。
       2. wait和notify对线程状态的影响:
            a) wait() 使当前线程释放对象锁,由执行状态转入阻塞状态.
            b) notifyAll() 唤醒所有在对象上阻塞的线程,被唤醒的所有线程由阻塞状态转入就绪(Runnable)状态.
                所有就绪状态的线程竞争对象锁。

          

WareHouse.java 

package org.mose.concurrency.threadmgt.no10_producerCustomer;

import java.util.ArrayList;
import java.util.List;

public class WareHouse {
	
	private int wareHouseSize;
	private List<Product> productList;
	
	
	public WareHouse(int size) {
		this.wareHouseSize = size;
		this.setProductList(new ArrayList<Product>(size));
	}

	public void addProduct(Product product) throws InterruptedException{
		synchronized(this){
			//防止虚假唤醒,所以使用while循环进行重复检测
			//e.g. 仓库满了之后,所有的生产者线程的addProduct动作进入阻塞/等待唤醒状态,
			//     如果此时消费者线程消费了一个产品,然后执行removeProduct()中的notifyAll,这样就唤醒了所有的生产者线程(所有生产者线程由阻塞状态转入就绪状态)。
			//     假如有两个生产者线程,这两个生产者被唤醒之后会竞争WareHouse对象的对象锁;
			//     假设第一个生产者线程得到了仓库的对象锁, 那么:
			//       1. 第一个生产者线程进入执行状态,从wait()之后的代码继续执行--插入一个产品。
			//       2. 第二个生产者线程仍然保持就绪状态,等待仓库的对象锁.
			//     第一个生产者线程往仓库插入一个产品之后,仓库满了,释放仓库对象锁,进入阻塞状态.
			//     如果第二个生产者线程此时得到第一个生产者线程释放的锁,就会进入执行状态, 但此时仓库是满的, 第二个生产者线程其实不应该被唤醒(应该继续wait), 这就是 虚假唤醒。
			//     通过while(isFull()),可以在线程被唤醒之后对条件/临界值进行重复检测,消除虚假唤醒从而避免错误的行为。
			//     反之, 如果仅适用if(isFull()), 第二个生产者线程会向已经满了的仓库里插入一个新产品,造成逻辑错误。
			while(isFull()){
				System.out.println("WareHouse full, " + Thread.currentThread().getName() + "waiting...");
				this.wait();
			}
			this.productList.add(product);
			System.out.println(Thread.currentThread().getName() + " generated a product");
			System.out.println("The are totally " + this.productList.size() + " products in WareHouse..");
			this.notifyAll();
		}
	}
	
	public void removeProduct() throws InterruptedException{
		synchronized(this){
			//防止虚假唤醒,所以使用while循环进行重复检测
			while(isEmpty()){
				System.out.println("WareHouse empty, " + Thread.currentThread().getName() + "waiting...");
				this.wait();
			}
			int size = this.productList.size();
			Product product = this.productList.get(size-1);
			this.productList.remove(product);
			System.out.println(Thread.currentThread().getName() + " consumed a product");
			System.out.println("The are totally " + this.productList.size() + " products in WareHouse..");
			this.notifyAll();
		}
	}
	
	public boolean isEmpty(){
		return this.productList.size() == 0;
	}
	
	public boolean isFull(){
		return this.productList.size() == this.wareHouseSize;
	}
	

	public List<Product> getProductList() {
		return productList;
	}
	public void setProductList(List<Product> productList) {
		this.productList = productList;
	}
}

Client.java

package org.mose.concurrency.threadmgt.no10_producerCustomer;

public class Client {
	public static void main(String[] args) {
		WareHouse wareHouse = new WareHouse(10);
		
		Producer producer1 = new Producer("Producer1", wareHouse);
		Producer producer2 = new Producer("Producer2", wareHouse);
		Producer producer3 = new Producer("Producer3", wareHouse);

		
		Customer customer1 = new Customer("Customer1", wareHouse);
		Customer customer2 = new Customer("Customer2", wareHouse);
		Customer customer3 = new Customer("Customer2", wareHouse);
		Customer customer4 = new Customer("Customer2", wareHouse);
		Customer customer5 = new Customer("Customer2", wareHouse);
		
		producer1.start();
		producer2.start();
		producer3.start();

		
		customer1.start();
		customer2.start();
		customer3.start();
		customer4.start();
		customer5.start();
		
	}
}

Customer.java

package org.mose.concurrency.threadmgt.no10_producerCustomer;

public class Customer extends Thread {
	
	private WareHouse wareHouse;
	
	public Customer(String customerName, WareHouse wareHouse){
		super(customerName);
		this.wareHouse = wareHouse;
	}
	
	@Override
	public void run() {
		try {
			while(true){
				Thread.sleep(100);
				consumeProduct();
			}
		} catch (InterruptedException e) {
				e.printStackTrace();
		}
	}
	
	
	public void consumeProduct() throws InterruptedException{
		wareHouse.removeProduct();
	}
}

Producer.java

package org.mose.concurrency.threadmgt.no10_producerCustomer;

public class Producer extends Thread {
	private WareHouse wareHouse;
	
	public Producer(String producerName, WareHouse wareHouse) {
		super(producerName);
		this.wareHouse = wareHouse;
	}
	
	@Override
	public void run() {
		try {
			while(true){
				Thread.sleep(100);
				generateProduct();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void generateProduct() throws InterruptedException{
		Product product = new Product();
		wareHouse.addProduct(product);
	}
}

一点思考

线程的调度需要结合wait()--当前线程转入阻塞转台并释放掉对象锁,notify()--唤醒在当前对象上阻塞的所有线程。

为什么不直接让notifyThenWait()方法实现上面两个功能:唤醒在此对象上阻塞的线程+当前线程转入阻塞状态并释放对象锁?这样编程模型会简化很多。

转载于:https://my.oschina.net/mose/blog/691425

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值