首頁 > 軟體

java中生產者消費者問題和程式碼案例

2022-02-08 13:01:53

應用場景

假設倉庫中只能存放一件產品,生產者將生產出來的產品放入倉庫,消費者將倉庫中產品取走消費

如果倉庫中沒有產品,則生產者將產品放入倉庫,否則停止生產並等待,直到倉庫中的產品被消費者取走為止

如果倉庫中放有產品,則消費者可以將產品取走消費,否則停止消費並等待,直到倉庫中再次放入產品為止

分析

這是一個執行緒同步問題,生產者和消費者共用同一個資源,並且生產者和消費者之間相互依賴,互為條件。

對於生產者,沒有生產產品之前,要通知消費者等待,而生產了產品之後,又需要馬上通知消費者消費

對於消費者,在消費之後,要通知生產者已經結束消費,需要生產新的產品以供消費

在生產者消費者問題中,僅有synchronized是不夠的

synchronized可阻止並行更新同一個共用資源,實現了同步

synchronized不能用來實現不同執行緒之間的訊息傳遞(通訊)

Java提供了幾個方法解決執行緒之間的通訊問題

方法名作用
wait()表示執行緒一直等待,直到其他執行緒通知,與sleep不同,wait()會釋放鎖
wait(long timeout)指定等待的毫秒數
notify()喚醒一個處於等待狀態的執行緒
notifyAll()喚醒同一個物件上所有呼叫wait()方法的執行緒,優先順序別高的執行緒優先排程

注意:均是Object類的方法,都只能在同步方法或者同步程式碼塊中使用,否則會丟擲異常

解決方法

管程法

生產者:負責生產資料的模組(可能是方法、物件、執行緒、程序)

消費者:負責處理資料的模組(可能是方法、物件、執行緒、程序)

緩衝區:消費者不能直接使用生產者的資料,他們之間有個“緩衝區”

生產者將生產好的資料放入緩衝區,消費者從緩衝區拿出資料

程式碼案例

//使用緩衝區解決生產者消費者模型
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();
    }
}
​
class Producer extends Thread{
    SynContainer synContainer;
    public Producer(SynContainer synContainer){
        this.synContainer = synContainer;
    }
    //  生產
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synContainer.push(new Chicken(i));
            System.out.println("生產了"+i+"只雞");
        }
    }
}
​
class Consumer extends Thread{
    SynContainer synContainer;
    public Consumer(SynContainer synContainer){
        this.synContainer = synContainer;
    }
    //  消費
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消費了-->"+synContainer.pop().id+"只雞");
        }
    }
}
​
//產品
class Chicken{
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}
​
//緩衝區
class SynContainer{
    Chicken[] chickens = new Chicken[10];
    int count = 0;
    //生產者放入產品
    public synchronized void push(Chicken chicken){
        //如果容器滿了就要等到消費者消費
        if (count==chickens.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //通知消費者消費,自身進入等待
        }
        //沒有滿的話則生產
        chickens[count] = chicken;
        count++;
        //通知消費者消費
        this.notifyAll();
    }
​
    //生產者消費產品
    public synchronized Chicken pop(){
        if (count==0){
            //等待生產者生產
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        Chicken chicken = chickens[count];
        //消費了,可以通知生產者繼續生產
        this.notifyAll();
        return chicken;
    }
}

訊號燈法

通過標誌位 true 或者 false 來進行判斷

程式碼案例

//訊號燈法測試生產者消費者模型 即標誌位
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Audiance(tv).start();
    }
​
}
​
//生產者-->演員
class Player extends Thread {
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("天天向上");
            }else {
                this.tv.play("抖音廣告");
            }
        }
    }
}
​
//消費者-->觀眾
class  Audiance extends Thread{
    TV tv;
    public Audiance(TV tv){
        this.tv = tv;
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
​
//產品-->節目
class TV{
    //演員表演 觀眾等待 T
    //觀眾觀看 演員等待 F
    String show;//表演的節目
    boolean flag = true;
​
    //表演
    public synchronized void play(String show){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演員表演了:"+show);
        //通知觀眾
        this.notifyAll();
        this.show = show;
        this.flag = !this.flag;
    }
​
    //觀看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("觀看了"+show);
        //通知演員表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

總結

到此這篇關於java中生產者消費者問題和程式碼案例的文章就介紹到這了,更多相關java生產者消費者問題內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com