JAVA生產者消費者的實現


春節回了趟老家,又體驗了一次流水席,由於桌席多,導致上菜慢,於是在等待間,總結了一下出菜流程的幾個特點:

1.有多個灶台,多個灶台都在同時做菜出來。

2.做出來的菜,會有專人用一個托盤端出來,每次端出來的菜(是同一個菜品)的數量不等。

3.由於端出來的菜可能不能滿足所有的桌數,所以,端菜人可能會隨機選擇幾桌(一般是就近原則,或者是主桌先端過去)上菜,其余的桌數繼續等待后面的端菜人出來。

以上3個條件,完全就是一個生產者消費者的場景,於是,把生產者消費者先來實現一下,然后再分析如何才能更快的上菜 :)

首先,我們把托盤給虛擬成一個資源池,表示這個托盤里是放菜的,當托盤里的菜大於1時,即有菜品被生產出來,端菜人就要端出去,當托盤里沒有菜時,外面所有的桌席都要等待:

(需要特別注意的是,這個資源池只能有一個實例化對象,就像托盤的數量是固定的一樣。)

public class ResourcePool {
	
	private int number = 0;

	public synchronized void producer(){
		try {
			while(number==3){
				this.wait();
			}
			number++;
			System.out.println("producer: "+number);
			this.notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void consumer(){
		try {
			while(number==0){
				this.wait();
			}
			number--;
			System.out.println("consumer: "+number);
			this.notifyAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

 其實,我們要有灶台,這個灶台是專門做菜的,做出來的菜,當然是全部放在了資源池(即托盤中),灶台是會有多個的,所以要繼承thread類:

public class ResourceProduce extends Thread{
	
	private ResourcePool rp;

	public ResourceProduce(ResourcePool rp) {
		this.rp = rp;
	}

	public void run() {
		rp.producer();
	}

}

 托盤中有了菜,就得端出去了,給送到外面的桌席上去,由於桌席是多桌,所以,也要繼承thread類:

public class ResourceConsumer extends Thread{
	
	private ResourcePool rp;

	public ResourceConsumer(ResourcePool rp) {
		this.rp = rp;
	}

	public void run() {
		rp.consumer();
	}

}

 這些基礎的設施都准備好后,我們的端菜人就出來了:

public class ResourceUtil {
	
	public void resource(){
		ResourcePool rp = new ResourcePool();
		for (int i = 0; i < 3; i++) {
			new ResourceProduce(rp).start();
		}
		for (int i = 0; i < 5; i++) {
			new ResourceConsumer(rp).start();
		}
	}
	
	public static void main(String[] args) {
		ResourceUtil ru = new ResourceUtil();
		ru.resource();
	}
	
}

 我們來看一下最后的輸出結果:

當只有三個灶台,而桌席有5桌時,程序就等待下去了,於是,當我們把灶台數改成5后,運行結果:

producer: 1
producer: 2
producer: 3
consumer: 2
producer: 3
consumer: 2
producer: 3
consumer: 2
consumer: 1
consumer: 0

 通過上面的程序運行,如果想上菜速度快,還是得加灶台,多加廚師,當然,這只是就這個場景簡單的分析了一下,可能還會有更復雜的因素沒考慮到,舉這個例子的主要意思,是想讓多多的理解一下生產者消費者模式,該模式我們平常可能用原生的比較少,但其實使用的場景一直都在用,比如線程池,連接池,等等。所以,知其然也知其所以然也很有必要,我們接着就代碼來說明一下這個實現代碼中的重點:

1.資源池有且只有一個。

2.synchronized,是鎖對象,簡單說一下:一個對象有且只有一把鎖,當有多個synchronized方法或代碼塊都向該對象申請鎖時,在同一時間,只會有一個線程得到該鎖並運行,其它的就被阻塞了。

3.wait,是指該線程等待,wait有一個很重要的點,就是釋放鎖,上面也說了synchronized在同一時間只會有一個線程得到該鎖並運行,所以,一旦wait后,就會釋放鎖,但當前線程等待下去,其它的線程再競爭這把鎖。

4.notifyAll是指喚醒當前對象的所有等待的線程。

5.所有喚醒的線程會同時去競爭這把鎖,但是JVM會隨機選擇一個線程並分配這把鎖給該線程。

6.上面的synchronized wait notifyAll都是對一個對象進行操作,但這三個都是用在了資源池的類里面,所以,這也是資源池有且只能有一個的原因。

 

后緒:至於生產者消費者能給我們測試帶來什么樣的幫助,我暫時還沒想到,但了解一下,出去面試時,有很大的可能性會被問到,有興趣的,就當作一種知識儲備吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM