1. 程式人生 > >Java提高——多執行緒(五)生產消費者問題

Java提高——多執行緒(五)生產消費者問題

生產者/消費者問題是個典型的多執行緒問題,類似於hello world對於一門程式語言而言,涉及的物件包括“生產者”、“消費者”、“倉庫”和“產品”。

該模型需要注意以下幾點:

1、生產者只有在倉庫未滿的時候生產,倉滿則停止生產。

2、消費者只有在倉庫有產品的情況下才能消費,空倉則等待。

3、當消費者發現沒有產品時通知生產者生產。

4、生產者在生產出可消費的產品時,通知等待的消費者去消費。

此模型需要結合Object類的wait、notify、notifyAll方法。

package Progress.Thread;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
/** * @date 2018/5/4 16:07 * 消費者/生產者問題 */ public class Test { public static void main(String[] args) { Godown godown = new Godown(100); Producer pro = new Producer(godown); Consumer con = new Consumer(godown); pro.produce(60); pro.produce(120); con.consume(90); con.consume(150); con.consume(110); } } /**倉庫*/
class Godown extends Thread{ /**倉庫容量*/ private int capacity; /**倉庫的實際容量*/ private int size; public Godown() { } public Godown(int capacity) { this.capacity = capacity; this.size = 0; } public synchronized void produce(int value){ //value表示想要生產的數量(有可能生產量太多需要多次生產)
try { while (value>0){ //庫存滿的時候等待消費者消費產品 while (size>=capacity) { wait(); } //獲取實際生產的數量(即庫存中增加的數量) //如果 庫存+想要生產的數量>總的容量,則 實際增量=總的容量-實際容量(此時倉庫滿倉) //否則 實際增量=想要生產的數量 int increment = (size+value)>capacity ? capacity - size : value; size = size + value; value -= increment; System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+increment); //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", //Thread.currentThread().getName(), increment, size,value); //通知消費者消費 notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void consume(int value){ try { //value表示要消費的數量(可能消費量太大數量不夠,要多次提供以供消費) while (value>0){ //庫存為0時,等待生產者生產 while (size<=0) { wait(); } //獲取實際消費的數量,即庫中實際減少的量 //如果 庫存<要消耗的量,則 實際消耗量 = 庫存 //否則,實際消耗的量 = 要消耗的量 int decent = size<value?size:value; size -= decent; value -= decent; System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+decent); //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", //Thread.currentThread().getName(), decent,value, size); //通知生產者生產 notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "Godown{" + "capacity=" + capacity + ", size=" + size + '}'; } } /**生產者*/ class Producer extends Thread{ private Godown godown; public Producer(Godown godown) { this.godown = godown; } //新建一個執行緒向倉庫中生產產品 public void produce(final int val){ new Thread(){ @Override public void run(){ godown.produce(val); } }.start(); } } /**消費者*/ class Consumer extends Thread{ private Godown godown; public Consumer(Godown godown) { this.godown = godown; } //新建一個執行緒去消耗產品 public void consume(final int val){ new Thread(){ @Override public void run(){ godown.consume(val); } }.start(); } }

生產者與倉庫關聯,當呼叫produce方法時,會生成一個執行緒向倉庫中生產產品。

消費者與倉庫關聯,當呼叫consume方法時,會生成一個執行緒從倉庫中獲得商品用於消費。

produce方法和consume方法都是synchronized的意味著進入就會獲得倉庫物件的同步鎖。也就是說同一時間,生產者和消費者執行緒只能有一個能夠執行,另一個必須等待,通過同步鎖實現了對“殘酷”的互斥訪問。

produce方法:當倉庫滿時,生產者執行緒等待,等待消費者消費商品之後,生產者才生產;生產之後通過notifyAll喚醒同步鎖上的所有執行緒,包括消費者執行緒,“通知消費者消費”

consume方法:當空倉的時候,消費者等待,等待生產者生產商品之後,消費者才消費;消費者通過notifyAll喚醒同步鎖上的所有執行緒,包括生產者執行緒,“通知生產者生產”