天天看點

Java生産者與消費者(下)

                                      本文來自 http://blog.csdn.net/liuxian13183/  ,引用必須注明出處!

上一講我們讓消費者和生産者都各停1毫秒,實際上大多并不是這樣的。第二講,我們講一個極端的例子和一個正常的例子,假設您已經看過上一講,我們隻改變Consumer和Producer,來看結果。 Java生産者與消費者(上)

名詞解釋

wait:目前線程不運作,處理等待狀态,等待notify;是以也不會往下執行。

您可能會有疑問前例中的為什麼要用while循環而非if語句?

主要是防止多線程操作時,例桌子已經占滿,兩個生産線程均處理等待狀态,但消費線程通過notifyAll打開其等待狀态,就造成生産過量的蛋糕,而導緻生産消費模式失敗,是以不能用if語句。

sleep:暫停目前線程若幹時間,之後繼續運作

Special Sample:生産者和消費者均不加sleep

Result:

Produce1 produce:1
Produce2 produce:2
Produce1 produce:3
Produce1 produce:4
Produce1 produce:5
Produce1 begin to wait !
Consumer1 consume:5
Consumer1 consume:4
Consumer1 consume:3
Consumer1 consume:2
Consumer1 consume:1
Consumer1 begin to wait !
Produce1 stop waiting !
Produce1 produce:1      
Produce1 produce:2
Produce2 produce:3
Produce2 produce:4
Produce2 produce:5
Produce2 begin to wait !
Consumer2 consume:5
Consumer2 consume:4
Consumer2 consume:3
Consumer2 consume:2
Consumer2 consume:1
Consumer2 begin to wait !
Produce2 stop waiting !
Produce2 produce:1
Produce2 produce:2
Produce2 produce:3
Produce2 produce:4
Produce2 produce:5
Produce2 begin to wait !
Produce1 begin to wait !
Consumer1 stop waiting !
Consumer1 consume:5
Consumer1 consume:4
Consumer1 consume:3
Consumer1 consume:2
Consumer1 consume:1
Consumer1 begin to wait !
Produce1 stop waiting !
Produce1 produce:1
Produce1 produce:2
Produce1 produce:3
Produce1 produce:4
Produce1 produce:5
Produce1 begin to wait !
Produce2 stop waiting !
Produce2 begin to wait !
Consumer2 stop waiting !
Consumer2 consume:5
Consumer2 consume:4
Consumer2 consume:3
Consumer2 consume:2
Consumer2 consume:1
Consumer2 begin to wait !
Produce2 stop waiting !
Produce2 produce:1
Produce2 produce:2
Produce2 produce:3
Produce2 produce:4
Produce2 produce:5
Produce2 begin to wait !
Produce1 stop waiting !
Produce1 begin to wait !
Consumer1 stop waiting !
Consumer1 consume:5
Consumer1 consume:4
Consumer1 consume:3
Consumer1 consume:2
Consumer1 consume:1
Consumer1 begin to wait !
Produce1 stop waiting !
Produce1 produce:1
Produce1 stop at last !      

解析:例子很極端,生産和消費均兩個線程,A線程在工作時B在等待;

A生産線程生産,B生産線程等待直到A生産完;

A消費線程消費,B消費線程等待直到A消費完;

B生産線程生産,A生産線程等待直到A生産完;

B消費線程消費,A消費線程等待直到A消費完;

……

這樣一個循環,失去多線程的意義所在(變成了單線程)!

正常的例子:生産可能隻需要1毫秒,消費需要2毫秒

Produce1 produce:1
Consumer1 consume:1
Consumer2 begin to wait !//此時Consumer2檢測到count為0,是以等待
Produce2 produce:1
Consumer2 stop waiting !//此時Consumer2檢測到count已經大于0,是以打開
Consumer2 consume:1
Produce1 produce:1//看,生産與消費是無序的。兩者分開,不管你在不在生産,我都要消費,隻要蛋糕還有;
Produce2 produce:2//不管你消費不消費,隻要桌子還有空的;它們兩個均對一種情況負責:桌子和蛋糕形成的映射關系!
Consumer2 consume:2
Consumer1 consume:1
Produce2 produce:1
Produce1 produce:2
Produce2 produce:3
Produce1 produce:4
Consumer1 consume:4
Consumer2 consume:3
Produce1 produce:3
Produce2 produce:4
Produce2 produce:5
Produce1 begin to wait !
Consumer1 consume:5
Produce1 stop waiting !
Produce1 produce:5
Produce2 begin to wait !
Consumer2 consume:5
Produce2 stop waiting !
Produce2 produce:5
Produce2 begin to wait !
Produce1 begin to wait !
Consumer2 consume:5
Produce1 stop waiting !
Produce1 produce:5
Produce2 stop waiting !
Produce2 begin to wait !
Consumer1 consume:5
Produce2 stop waiting !
Produce2 produce:5
Produce1 begin to wait !
Produce2 begin to wait !
Consumer1 consume:5
Consumer1 stop at last !      

解析:這次我讓執行25次即退出虛拟機。

大家可以看到,本次兩個生産線程和兩個消費線程,在生産和消費的時候都是無序的,無論你要不要停,我都要工作,不能讓一個線程一直抓住鎖不放,其實這才是多線程本質。

代碼如下:

Producer:

public class Producer extends Thread {
	Table table;
	String threadName;

	public Producer(String string, Table table) {
		// TODO Auto-generated constructor stub
		this.table = table;
		this.threadName = string;
		this.setName(threadName);
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		try {
			while (true) {
				table.produce(threadName);
				sleep(1);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
      
消費者:
public class Consumer extends Thread {
	Table table;
	String threadName;

	public Consumer(String string, Table table) {
		// TODO Auto-generated constructor stub
		this.table = table;
		this.threadName = string;
		this.setName(threadName);
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		try {
			while (true) {
				table.consume(threadName);
				sleep(2);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}