生產者消費者

生產者和消費者是一種經典的供求案例,生產者和消費者線程之間的關係涉及線程之間的交互。生產者線程產生的數據項,將來會被消費者消費,每個生產出來的數據會被保存在一個共享的倉庫中。

假設線程以不同的速度進行,那麼將會導致消費者可能獲取到還未生產的數據,生產者也有可能在消費者還未取走前一條數據的前提下繼續生產,這樣就會造成混亂。

爲了克服這種混亂,生產者線程必須等待,知道消費者取走之後通知生產者,等待解除,可以生產。如果生產者未生產數據,消費者就要一直等待,直到生產者生產完畢,喚醒消費者線程,取走生產好的數據。

生產者消費者
以下爲代碼實現:

package my_application;

public class ProducerConsumer {

    public static void main(String[] args) {
        ShareStorage s = new ShareStorage();
        new Producer(s).start();
        new Consumer(s).start();
    }
}
class ShareStorage{
    private char c;
        //此實例變量跟蹤兩個條件,生產者等待消費者消費一個數據,消費者等待生產者生產一個數據
    private volatile boolean flag = true;
    synchronized void setShareData(char c){
        while (!flag) {//消費者未取走,則生產者等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.c = c;
        flag = false;//設置標誌位爲false,告訴消費者可以消費,此時生產者等待
        notify();
    }
    synchronized char getShareData(){
        while (flag) {//flag = true ,表示消費者等待,此時生產者生產
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        flag = true;
        notify();
        return c;
    }
}
class Producer extends Thread{
    private final ShareStorage shareStorage;
    Producer(ShareStorage shareStorage){
        this.shareStorage = shareStorage;
    }
    @Override
    public void run() {
        for (char ch = 'A'; ch < 'Z'; ch++) {
            shareStorage.setShareData(ch);
            System.out.println(ch + ",生產者生產");
        }
    }
}
class Consumer extends Thread{
    private final ShareStorage shareStorage;
    Consumer(ShareStorage shareStorage){
        this.shareStorage = shareStorage;
    }
    @Override
    public void run() {
        char ch;
        do {
            ch = shareStorage.getShareData();
            System.out.println(ch + ",消費者消費");
        } while (ch != 'Z');
    }
}

此時的輸出結果可能爲:

O,生產者生產
O,消費者消費
P,生產者生產
P,消費者消費

或者

A,生產者生產
B,生產者生產
A,消費者消費
B,消費者消費

這樣的輸出並不表示消費者和生產者線程沒有同步,這是因爲setShareData(),getShareData()之後的輸出沒有被同步造成的結果,爲了解決這個問題,加上同步塊就可以完美解決這個問題了。見下面修改後的代碼:

package my_application;

public class ProducerConsumer {

    public static void main(String[] args) {
        ShareStorage s = new ShareStorage();
        new Producer(s).start();
        new Consumer(s).start();
    }
}
class ShareStorage{
    private char c;
    private volatile boolean flag = true;//寫入取走標記
    synchronized void setShareData(char c){
        while (!flag) {//消費者未取走,則生產者等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.c = c;
        flag = false;//設置標誌位爲false,告訴消費者可以消費,此時生產者等待
        notify();
    }
    synchronized char getShareData(){
        while (flag) {//flag = true ,表示消費者等待,此時生產者生產
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        flag = true;
        notify();
        return c;
    }
}
class Producer extends Thread{
    private final ShareStorage shareStorage;
    Producer(ShareStorage shareStorage){
        this.shareStorage = shareStorage;
    }
    @Override
    public void run() {
        for (char ch = 'A'; ch < 'Z'; ch++) {
            synchronized (shareStorage) {
                shareStorage.setShareData(ch);
                System.out.println(ch + ",生產者生產");
            }
        }
    }
}
class Consumer extends Thread{
    private final ShareStorage shareStorage;
    Consumer(ShareStorage shareStorage){
        this.shareStorage = shareStorage;
    }
    @Override
    public void run() {
        char ch;
        do {
            synchronized (shareStorage) {
                ch = shareStorage.getShareData();
                System.out.println(ch + ",消費者消費");
            }
        } while (ch != 'Z');
    }
}

輸出結果:

A,生產者生產
A,消費者消費
B,生產者生產
B,消費者消費
C,生產者生產
C,消費者消費
D,生產者生產
D,消費者消費
E,生產者生產
E,消費者消費
...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章