Java多線程--生產者與消費者問題

說明

Java中,線程之間的通信主要是由java.lang.Object類提供的waitnotifynotifyAll這3個方法來完成:

①對象的wait方法被調用後,線程進入對象的等待隊列中,並釋放對象鎖,其它線程可以競爭使用此對象鎖;sleep方法使得一個線程進入睡眠狀態,但是線程所佔有的資源並沒有釋放。

②當對象的notify方法被調用,該方法會從對象的等待隊列中隨機取出一個線程來喚醒;notifyAll是喚醒等待隊列中所有線程,這些線程會與其它正在執行的線程共同競爭對象鎖。

③wait、notifynotifyAll這3個方法只能出現在synchronized作用的範圍內。當多個線程等待同一對象,而它們等待等待的條件不同時,應該使用notifyAll方法。notifyAll會導致性能下降,因爲不必要的線程也會被喚醒。而notify喚醒的線程有可能不是所期望的線程。


生產者與消費者問題

這裏,將使用某類資源(用類Goods表示)的線程稱爲消費者Consumer,產生同類資源的線程稱爲Producer。要想使得Consumer和Producer能夠協同工作,則它們應該遵循如下規則:

(1)只要緩衝區buffer有空間,生產者Producer就可向其中存放資源;當緩衝區buffer已滿時,則讓生產者Producer等待,放棄自己已獲取的對象鎖,進入等待隊列;

(2)只要緩衝區中有資源可用,消費者Consumer就可以從buffer中取出資源;當buffer爲空時,則讓Consumer等待,放棄自己已獲取的對象鎖,進入等待隊列;

(3)Producer和Consumer不能同時讀、寫buffer。所以對buffer進行操作的方法increase(生產資源)和decrease(消費資源)均需要使用synchronized進行同步。


/**
 * Goods類,表示共享資源
 */
package com.hh.Producer.Consumer;

public class Goods {
	private final int SIZE = 5;
	private int buffer = 0;

	/**
	 * 共享資源增加
	 */
	public synchronized int increase() {
		if (buffer < SIZE) {
			++buffer;
			notify(); // 通知消費者可以消費
		} else {
			try {
				wait(); // 生成者線程等待,直到消費者線程發出notify通知
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return buffer;
	}

	/**
	 * 共享資源減少
	 */
	public synchronized int decrease() {
		if (buffer > 0) {
			--buffer;
			notify(); // 通知生產者可以生產
		} else {
			try {
				wait(); // 消費者線程等待,直到生產者線程發出notify通知
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return buffer;
	}

	public int getGoodsSize() {
		return SIZE;
	}

}


/**
 * Producer類,模擬生產者線程
 */
package com.hh.Producer.Consumer;

public class Producer implements Runnable {

	private Goods goods;

	public Producer(Goods goods) {
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			int goodsCount = goods.increase();
			if (goodsCount != goods.getGoodsSize()) {
				System.out.println("生產者生產了一件商品,目前商品數:" + goodsCount);
			} else {
				System.out.println("商品已滿,生產者等待");
			}
			try {
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

/**
 * Consumer類,模擬消費者
 */
package com.hh.Producer.Consumer;

public class Consumer implements Runnable {

	private Goods goods;

	public Consumer(Goods goods) {
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			int goodsCount = goods.decrease();
			if (goodsCount != 0) {
				System.out.println("消費者消費了一件商品,目前商品數:" + goodsCount);
			} else {
				System.out.println("商品已空,消費者等待");
			}
			try {
				Thread.sleep((int) (Math.random() * 1000));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

/**
 * 測試生產者Producer與消費者Consumer線程之間的通信
 */
package com.hh.Producer.Consumer;

public class TestMain {
	
	public static void main(String[] args) {
		Goods goods = new Goods();
		Producer producer = new Producer(goods);
		Consumer consumer = new Consumer(goods);

		new Thread(producer).start();
		new Thread(consumer).start();
	}

}

注意:是在共享資源Goods類中進行wait和notify操作,並不是在Producer或者Consumer類中。

對於這樣的問題,一定要自己動手寫示例代碼,這樣才能較好的理解線程之間的通信問題。


發佈了83 篇原創文章 · 獲贊 25 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章