wait與notifyAll實現簡單的生產者消費者模式

前面轉載了一篇用BlockingQueue實現生產者消費者模式的文章。文中提到了另外一種實現方式,利用對象的wait以及notifyAll方法。

先貼上代碼:

package com.demo.test;

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

public class ProducerConsumer {
	public static void main(String[] args) {
		// 建立倉庫
		MyStack stack = new MyStack();
		// 生產者
		Producer producer = new Producer(stack);
		// 消費者
		Consumer consumer = new Consumer(stack);

		// 開始生產
		new Thread(producer).start();
		// 開始消費
		new Thread(consumer).start();
	}

	/**
	 * 產品類
	 * 
	 * @author kk
	 * 
	 */
	private static class Product {
		private int id;

		public Product(int productId) {
			this.id = productId;
		}

		@Override
		public String toString() {
			return "product" + id;
		}
	}

	/**
	 * 生產者, 負責生產產品
	 * 
	 * @author kk
	 * 
	 */
	private static class Producer implements Runnable {
		private String name = "生產者";
		private MyStack myStack;

		public Producer(MyStack stack) {
			this.myStack = stack;
		}

		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				// System.out.println("生產" + i);
				// 生產產品
				Product product = new Product(i);
				// 入庫
				myStack.push(product);
				// 休息下
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 消費者,消費產品
	 * 
	 * @author kk
	 * 
	 */
	private static class Consumer implements Runnable {
		private String name = "消費者";
		private MyStack myStack;

		public Consumer(MyStack stack) {
			this.myStack = stack;
		}

		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				// System.out.println("消費" + i);
				// 產品出庫
				Product product = myStack.pop();
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 實現自己的倉庫,用來存放產品
	 * 
	 * @author kk
	 * 
	 */
	private static class MyStack {
		private List<Product> products = new ArrayList<Product>();// 存放產品

		/**
		 * 從倉庫中獲取產品來消費
		 * 
		 * @return
		 */
		public synchronized Product pop() {
			// 如果倉庫已經消費空了,那你就等着新品上市吧
			if (products.size() == 0) {
				System.out.println("倉庫已經空了,等待生產...");
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.notifyAll();// 通知生產
			// 倉庫沒空,從倉庫中拿出一個產品
			Product product = products.get(0);
			products.remove(product);
			System.out.println("產品出庫:" + product);
			return product;
		}

		/**
		 * 將生產者生產的產品入庫
		 * 
		 * @param producedProduct
		 */
		public synchronized void push(Product producedProduct) {
			// 如果當前倉庫已經滿了
			if (products.size() >= 5) {
				System.out.println("倉庫已經滿啦,沒地方放了,快停止生產吧,通知消費者來消費");
				try {
					this.wait();// 停止生產
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.notifyAll();// 通知消費
			// 倉庫未滿,既然生產了就入庫吧
			products.add(producedProduct);
			System.out.println("\t產品入庫:" + producedProduct);
		}

	}
}


運行結果:

倉庫已經空了,等待生產...
	產品入庫:product0
產品出庫:product0
	產品入庫:product1
	產品入庫:product2
產品出庫:product1
	產品入庫:product3
	產品入庫:product4
	產品入庫:product5
產品出庫:product2
	產品入庫:product6
	產品入庫:product7
產品出庫:product3
	產品入庫:product8
倉庫已經滿啦,沒地方放了,快停止生產吧,通知消費者來消費
產品出庫:product4
	產品入庫:product9
產品出庫:product5
產品出庫:product6
產品出庫:product7
產品出庫:product8
產品出庫:product9


Notes:

1,wait()必須在synchronized 函數或者代碼塊裏面,wait會讓synchronized函數或者代碼塊所在線程暫時的停止運行,釋放鎖,喪失控制權,該線程進入等待,

讓其它的線程有機會獲得控制權,在條件滿足的情況下調用notifyAll()來喚醒正在wait的線程,等待獲取cpu時間,獲取到後,繼續往下執行;

2,生產者Producer和消費者Consumer要共用倉庫MyStack;

3,notifyAll()方法是喚醒其他正在等待的線程,並不讓出自己的cpu時間,還是會繼續運行;

4,倉庫空了,消費者wait,通知生產者趕緊生產notifyAll。倉庫滿了,生產者wait,通知消費者趕緊消費notifyAll;




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章