天天看點

一文速解生産者-消費者模式問題 | 帶你學《Java語言進階特性》之十二

上一篇:經典案例:生産者-消費者模型 | 帶你學《Java語言進階特性》之十一

【本節目标】

通過閱讀本節内容,你将通過實操代碼,進一步掌握synchronized關鍵字的使用方法,解決同步問題,學會使用Object類中提供的相關方法解決重複問題。

解決資料同步

如果要解決問題,首先解決的就是資料同步的處理問題,如果要想解決資料同步最簡單的做法就是使用synchronized關鍵字定義同步代碼塊或同步方法,于是這個時候對于同步的處理就可以直接在Message類中完成。

範例:解決同步操作

class Message {
    private String title;
    private String content;
    public synchronized void set(String title, String content) {
        this.title = title;
        try {
            Thread.sleep(100);     //模拟網絡延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.content = content;
    }
    public synchronized String get() {
        try {
            Thread.sleep(10);     //模拟網絡延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return this.title + " - " + this.content;
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Message msg = new Message();
        new Thread(new Producer(msg)).start();    //啟動生産者線程
        new Thread(new Consumer(msg)).start();   //啟動消費者線程
    }
}
class Producer implements Runnable {
    private Message msg;
    public Producer(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            if (x % 2 == 0) {
                this.msg.set("王健", "宇宙大帥哥");
            } else {
                this.msg.set("小高", "猥瑣第一人,常态保持");
            }
        }
    }
}
class Consumer implements Runnable {
    private Message msg;
    public Consumer(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(this.msg.get());
        }
    }
}           
一文速解生産者-消費者模式問題 | 帶你學《Java語言進階特性》之十二

圖一 執行結果圖

在進行同步處理的時候肯定需要有一個同步的處理對象,那麼此時肯定要将同步操作交由Message處理是最合适的。這時資料已經可以正常保持一緻了,但是對于重複操作的問題依然存在。

線程等待與喚醒

如果說現在要想解決生産者與消費者的問題,那麼最好的解決方案就是使用等待與喚醒機制。而對于等待與喚醒的操作機制主要依靠是Object類中提供的方法處理的:

等待機制:

1、死等:

public final void wait() throws InterruptedException;

2、設定等待時間(毫秒):

public final void wait(long timeout) throws InterruptedException;

3、設定等待時間(納秒):

public final void wait(long timeout, int nanos) throws InterruptedException;

喚醒第一個等待線程:public final void notify();

喚醒全部等待線程:public final void notifyAll();

如果此時有若幹個等待線程的話,那麼notify()表示的是喚醒第一個等待的,而其他的線程繼續等待,而notifyAll()表示會喚醒所有等待的線程,哪個線程的優先級高就有可能先執行。

對于目前的問題主要的解決應該通過Message類完成處理。

範例:修改Message類

public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Message msg = new Message();
        new Thread(new Producer(msg)).start();       //啟動生産者線程
        new Thread(new Consumer(msg)).start();      //啟動消費者線程
    }
}
class Producer implements Runnable {
    private Message msg;
    public Producer(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            if (x % 2 == 0) {
                this.msg.set("王健", "宇宙大帥哥");
            } else {
                this.msg.set("小高", "猥瑣第一人,常态保持");
            }
        }
    }
}
class Consumer implements Runnable {
    private Message msg;
    public Consumer(Message msg) {
        this.msg = msg;
    }
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(this.msg.get());
        }
    }
}
class Message {
    private String title;
    private String content;
    private boolean flag = true;              //表示生産或消費的形式
    //flag = true:允許生産,但不允許消費
    //flag = false:允許消費,但不允許生産
    public synchronized void set(String title, String content) {
        if(this.flag==false){             //無法進行生産,應該等待被消費
            try {
                super.wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        this.title = title;
        try {
            Thread.sleep(100);             //模拟網絡延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.content = content;
        this.flag=false;               //已經生産過了
        super.notify();               //喚醒等待的線程
    }
    public synchronized String get() {
        if(this.flag==true){          //還未生産,需要等待
            try {
                super.wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(10);           //模拟網絡延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            return this.title + " - " + this.content;
        }finally {           //不管如何都要執行
            this.flag=true;           //繼續生産
            super.notify();            //喚醒等待的線程
        }
    }
}           
一文速解生産者-消費者模式問題 | 帶你學《Java語言進階特性》之十二

圖二 線程的等待與喚醒

這種處理形式就是在進行多線程開發過程之中最原始的處理方案,整個的等待、同步、喚醒機制都由開發者自行通過原生代碼實作控制。

想學習更多的Java的課程嗎?從小白到大神,從入門到精通,更多精彩不容錯過!免費為您提供更多的學習資源。

本内容視訊來源于

阿裡雲大學 下一篇:教你在停車前為線程配個“保镖” | 帶你學《Java語言進階特性》之十三 更多Java面向對象程式設計文章檢視此處