春節回了趟老家,又體驗了一次流水席,因爲桌席多,致使上菜慢,因而在等待間,總結了一下出菜流程的幾個特色:java
1.有多個竈臺,多個竈臺都在同時作菜出來。面試
2.作出來的菜,會有專人用一個托盤端出來,每次端出來的菜(是同一個菜品)的數量不等。測試
3.因爲端出來的菜可能不能知足全部的桌數,因此,端菜人可能會隨機選擇幾桌(通常是就近原則,或者是主桌先端過去)上菜,其他的桌數繼續等待後面的端菜人出來。this
以上3個條件,徹底就是一個生產者消費者的場景,因而,把生產者消費者先來實現一下,而後再分析如何才能更快的上菜 :)線程
首先,咱們把托盤給虛擬成一個資源池,表示這個托盤裏是放菜的,當托盤裏的菜大於1時,即有菜品被生產出來,端菜人就要端出去,當托盤裏沒有菜時,外面全部的桌席都要等待:對象
(須要特別注意的是,這個資源池只能有一個實例化對象,就像托盤的數量是固定的同樣。)blog
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(); } }
這些基礎的設施都準備好後,咱們的端菜人就出來了:it
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都是對一個對象進行操做,但這三個都是用在了資源池的類裏面,因此,這也是資源池有且只能有一個的緣由。
後緒:至於生產者消費者能給咱們測試帶來什麼樣的幫助,我暫時還沒想到,但瞭解一下,出去面試時,有很大的可能性會被問到,有興趣的,就看成一種知識儲備吧。