JAVA 多线程经典案例-生产者消费者模型【使用wait/notify实现】

  • 生产者消费者模型实现细节
  • 功能实现说明
  • 代码实现
  • 写在最后的话

生产者消费者模型实现细节

生产者生产产品到公共仓库,消费者消费公共仓库中产品。
情况一:当公共仓库产品达到仓库容量上限,生产者停止生产;
情况二:当公共仓库没有产品,或达到设置的仓库容量下限,消费者停止消费;
情况三:当公共仓库产品达到容量上限时,消费者消费一次,就可以让生产者继续生产;
情况四:当公共仓库没有产品时,生产者生产一次,就可以让消费者继续消费;

功能实现说明

  1. 公共仓库 ,包含“仓库容量上限”、“仓库容量下限”、“当前容量”基础属性,及“入库”、“出库”基础方法;
  2. 生产者 ,包含“所属仓库”、“每次生产数量”基础属性,及“生产存入仓库”基础方法;
  3. 消费者 ,包含“所属仓库”、“每次消费数量”基础属性,及“仓库取出消费”基础方法;

代码实现

  1. 公共仓库 ,抽取“入库”、“出库”基础方法作为接口,后续可通过实现该接口,重写方法来实现;
    (1)公共仓库接口
/**
 * @author Evan
 * @date 2018年12月11日
 * @method putIn(int num) //往仓库中存入指定数量的产品
 * @method outOf(int num) //从仓库中取出制定数量的产品
 */
interface AbstractStorage {
	void putIn(int num);
	void outOf(int num);
}

(2)仓库实现类
仓库类只需判断库存量是否达到存入/取出阈值,从而限制生产者/消费者的存入/取出,不需要使用循环来判断;

/**
 * @author Evan
 * @date 2018年12月11日
 * @param MAX_PRODUCT //仓库容量上限
 * @param MIN_PRODUCT //仓库容量下限
 * @param stock       //仓库当前容量,属于共享资源
 * @method putIn(int)  //当(库存+即将存入)大于容量上限,停止存入
 * @method outOf(int)  //当(库存-即将取出)小于容量上限,停止取出
 */
class Storage implements AbstractStorage {
	public final int MAX_PRODUCT = 10;
	public final int MIN_PRODUCT = 0;
	private int stock;

	@Override
	public synchronized void putIn(int num) {
		if ((stock + num) >= MAX_PRODUCT) {
			System.out.println("库存已满!");
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			stock += num;
			System.out.println(Thread.currentThread().getName() + "存入,库存:" + stock);
			notifyAll();
		}
	}

	@Override
	public synchronized void outOf(int num) {
		if ((stock - num) <= MIN_PRODUCT) {
			System.out.println("库存已空!");
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} else {
			stock -= num;
			System.out.println(Thread.currentThread().getName() + "取出,库存:" + stock);
			notifyAll();
		}
	}

	public int getStock() {
		return stock;
	}

	public void setStock(int stock) {
		this.stock = stock;
	}
}
  1. 生产者
    使用实现Runnable接口方式创建生产者线程,将生产者生产操作放到重写的run()方法中,实现生产者线程生产;
/**
 * @author Evan
 * @Data 2018年12月11日
 * @param storage //公共仓库
 * @param num     //生产数量
 * @constructor Produce(AbstractStorage)   //构造器
 * @method prod(int)  //生产者生产方法
 * @method run()  //生产者线程执行方法
 * @method setNum(int)  //设置生产者每次生产数量
 */
class Producer implements Runnable {
	private AbstractStorage storage;
	private int num;

	public Producer(AbstractStorage storage) {
		this.storage = storage;
	}

	@Override
	public void run() {
		prod(num);
	}

	private void prod(int num) {
		while (true) {
			try {
				Thread.sleep(100);  //需要添加延时,不然生产者线程获得执行权时,一瞬间即可生产到仓库上限值
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			storage.putIn(num);
		}
	}

	public void setNum(int num) {
		this.num = num;
	}
}
  1. 消费者
    使用实现Runnable接口方式创建消费者线程,将消费者消费操作放到重写的run()方法中,实现消费者线程消费;
/**
 * @author Evan
 * @Data 2018年12月11日
 * @param storage //公共仓库
 * @param num     //生产数量
 * @constructor Consume(AbstractStorage)   //构造器
 * @method cons(int)  //消费者生产方法
 * @method run()  //消费者线程执行方法
 * @method setNum(int)  //设置消费者每次生产数量
 */
class Consumer implements Runnable {
	private AbstractStorage storage;
	private int num;

	public Consumer(AbstractStorage storage) {
		this.storage = storage;
	}

	@Override
	public void run() {
		cons(num);
	}

	private void cons(int num) {
		while (true) {
			try {
				Thread.sleep(100); //需要添加延时,不然消费者线程获得执行权时,一瞬间即可消费到仓库下限值
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			storage.outOf(num);
		}
	}

	public void setNum(int num) {
		this.num = num;
	}
  1. 测试类
/**
 * @author Evan
 * @Date 2018年12月11日
 * @param storage  //创建公共仓库
 * @param producer       //创建生产者线程
 * @param consumer       //创建消费者线程
 */
public class TestA {
	public static void main(String[] args) {
		AbstractStorage storage = new Storage();

		Producer producer = new Producer(storage);
		Consumer consumer = new Consumer(storage);

		producer.setNum(2);
		consumer.setNum(1);

		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);

		t1.setName("producer");
		t2.setName("consumer");

		t1.start();
		t2.start();

	}
}

测试输出

java多线程全部事务回滚_java多线程全部事务回滚

写在最后的话

使用wait/notify方法实现,很多时候会出现一些奇怪的问题,例如:
1、两者都持有锁且等待对方释放锁,造成死锁的情况;
2、对未持有锁的对象进行释放锁,导致IllegalMonitorStateException异常;
3、加锁时,双方所加的锁并非同一把锁,导致数据异常;

关于生产者/消费者模型,现在更多是使用阻塞队列(Queue)的方式来实现,后续会接着去学习那一块知识。

【注】以上为本人通过网上视频教程以及实例代码整理所得,如果出错以及更好的提议,欢迎留言指正,一起学习。