多线程“基础篇”11之 生产消费者问题
本章,會對“生產/消費者問題”進行討論。涉及到的內容包括:
1. 生產/消費者模型
2. 生產/消費者實現?
1. 生產/消費者模型
生產/消費者問題是個非常典型的多線程問題,涉及到的對象包括“生產者”、“消費者”、“倉庫”和“產品”。他們之間的關系如下:
(01) 生產者僅僅在倉儲未滿時候生產,倉滿則停止生產。
(02) 消費者僅僅在倉儲有產品時候才能消費,倉空則等待。
(03) 當消費者發現倉儲沒產品可消費時候會通知生產者生產。
(04) 生產者在生產出可消費產品時候,應該通知等待的消費者去消費。
?
2. 生產/消費者實現
下面通過wait()/notify()方式實現該模型(后面在學習了線程池相關內容之后,再通過其它方式實現生產/消費者模型)。源碼如下:
//Demo1.java //倉庫 class?Depot?{private?int?capacity;?//?倉庫的容量private?int?size;?//?倉庫的實際數量public?Depot(int?capacity)?{this.capacity?=?capacity;this.size?=?0;}public?synchronized?void?produce(int?val)?{try?{//?left?表示“想要生產的數量”(有可能生產量太多,需多此生產)int?left?=?val;while?(left?>?0)?{//?庫存已滿時,等待“消費者”消費產品。while?(size?>=?capacity)wait();//?獲取“實際生產的數量”(即庫存中新增的數量)//?如果“庫存”+“想要生產的數量”>“總的容量”,則“實際增量”=“總的容量”-“當前容量”。(此時填滿倉庫)//?否則“實際增量”=“想要生產的數量”int?inc?=?(size?+?left)?>?capacity???(capacity?-?size)?:?left;size?+=?inc;left?-=?inc;System.out.printf("%s?produce(%3d)?-->?left=%3d,?inc=%3d,?size=%3d\n",Thread.currentThread().getName(),?val,?left,?inc,?size);//?通知“消費者”可以消費了。notifyAll();}}?catch?(InterruptedException?e)?{}}public?synchronized?void?consume(int?val)?{try?{//?left?表示“客戶要消費數量”(有可能消費量太大,庫存不夠,需多此消費)int?left?=?val;while?(left?>?0)?{//?庫存為0時,等待“生產者”生產產品。while?(size?<=?0)wait();//?獲取“實際消費的數量”(即庫存中實際減少的數量)//?如果“庫存”<“客戶要消費的數量”,則“實際消費量”=“庫存”;//?否則,“實際消費量”=“客戶要消費的數量”。int?dec?=?(size?<?left)???size?:?left;size?-=?dec;left?-=?dec;System.out.printf("%s?consume(%3d)?<--?left=%3d,?dec=%3d,?size=%3d\n",Thread.currentThread().getName(),?val,?left,?dec,?size);notifyAll();}}?catch?(InterruptedException?e)?{}}public?String?toString()?{return?"capacity:"?+?capacity?+?",?actual?size:"?+?size;} }//?生產者 class?Producer?{private?Depot?depot;public?Producer(Depot?depot)?{this.depot?=?depot;}//?消費產品:新建一個線程向倉庫中生產產品。public?void?produce(final?int?val)?{new?Thread()?{public?void?run()?{depot.produce(val);}}.start();} }//?消費者 class?Customer?{private?Depot?depot;public?Customer(Depot?depot)?{this.depot?=?depot;}//?消費產品:新建一個線程從倉庫中消費產品。public?void?consume(final?int?val)?{new?Thread()?{public?void?run()?{depot.consume(val);}}.start();} }public?class?Demo1?{public?static?void?main(String[]?args)?{Depot?mDepot?=?new?Depot(100);Producer?mPro?=?new?Producer(mDepot);Customer?mCus?=?new?Customer(mDepot);mPro.produce(60);mPro.produce(120);mCus.consume(90);mCus.consume(150);mPro.produce(110);} }說明:
(01) Producer是“生產者”類,它與“倉庫(depot)”關聯。當調用“生產者”的produce()方法時,它會新建一個線程并向“倉庫”中生產產品。
(02) Customer是“消費者”類,它與“倉庫(depot)”關聯。當調用“消費者”的consume()方法時,它會新建一個線程并消費“倉庫”中的產品。
(03) Depot是“倉庫”類,倉庫中記錄“倉庫的容量(capacity)”以及“倉庫中當前產品數目(size)”。
? ? ? ? “倉庫”類的生產方法produce()和消費方法consume()方法都是synchronized方法,進入synchronized方法體,意味著這個線程獲取到了該“倉庫”對象的同步鎖。這也就是說,同一時間,生產者和消費者線程只能有一個能運行。通過同步鎖,實現了對“殘酷”的互斥訪問。
? ? ? ?對于生產方法produce()而言:當倉庫滿時,生產者線程等待,需要等待消費者消費產品之后,生產線程才能生產;生產者線程生產完產品之后,會通過notifyAll()喚醒同步鎖上的所有線程,包括“消費者線程”,即我們所說的“通知消費者進行消費”。
? ? ??對于消費方法consume()而言:當倉庫為空時,消費者線程等待,需要等待生產者生產產品之后,消費者線程才能消費;消費者線程消費完產品之后,會通過notifyAll()喚醒同步鎖上的所有線程,包括“生產者線程”,即我們所說的“通知生產者進行生產”。
(某一次)運行結果:
Thread-0?produce(?60)?-->?left=??0,?inc=?60,?size=?60Thread-4?produce(110)?-->?left=?70,?inc=?40,?size=100Thread-2?consume(?90)?<--?left=??0,?dec=?90,?size=?10Thread-3?consume(150)?<--?left=140,?dec=?10,?size=??0Thread-1?produce(120)?-->?left=?20,?inc=100,?size=100Thread-3?consume(150)?<--?left=?40,?dec=100,?size=??0Thread-4?produce(110)?-->?left=??0,?inc=?70,?size=?70Thread-3?consume(150)?<--?left=??0,?dec=?40,?size=?30Thread-1?produce(120)?-->?left=??0,?inc=?20,?size=?50
轉載于:https://blog.51cto.com/sky2012/1753067
總結
以上是生活随笔為你收集整理的多线程“基础篇”11之 生产消费者问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Guava 是个风火轮之基础工具(4)
- 下一篇: spring-data-jpa 查询视图