1. 程式人生 > 其它 >Java多執行緒——簡單的生產者消費者問題練習

Java多執行緒——簡單的生產者消費者問題練習

技術標籤:java問題java

Java多執行緒生產者消費者

Java的多執行緒學習中,因為有練習的要求,扔個生產者消費者的程式碼放上來:
先解釋一下,裡面有三個類,分別是cook(廚師),waiter(服務生)和food,廚師生產食物,而服務生則端走食物。那麼先寫一個用鎖去同步的多執行緒,程式碼其實很簡單,就是廚師對食物進行更改,直接修改食物的型別(手撕雞或者滷豬腳),就算是食物生產完成了。


public class TestThread {

    public static void main(String[] args){
        food f = new food
(); cook c = new cook(f); waiter w = new waiter(f); Thread t1 = new Thread(c); Thread t2 = new Thread(w); t1.start(); t2.start(); } } class cook implements Runnable{ food f; static final int count = 5; cook(food f){ this.f = f;
} @Override public void run() { for (int i = 0; i < count; i++) { f.l.lock(); if(i % 2 == 0) { f.setName("手撕雞"); f.setTaste("麻辣味"); f.setFood(); }else{ f.setName
("滷豬腳"); f.setTaste("滷煮味"); f.setFood(); } f.l.unlock(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class waiter implements Runnable{ food f; static final int count = 5; waiter(food f){ this.f = f; } @Override public void run() { for (int i = 0; i < count; i++) { f.l.lock(); f.getFood(); f.l.unlock(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class food{ String name; String taste; Lock l; public food() { l = new ReentrantLock(); } public void setFood(){ System.out.println("廚師製作了" + taste + name); } public void getFood(){ System.out.println("服務員端走了" + taste + name); } public food(String name, String taste) { this.name = name; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.taste = taste; l = new ReentrantLock(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTaste() { return taste; } public void setTaste(String taste) { this.taste = taste; } }

那麼這段程式碼執行的一個結果是這樣的:
在這裡插入圖片描述
可以看到,我們雖然設定了執行緒同步(用的是顯式鎖),但是沒規定是廚師先上菜還是服務員先端盤子,於是乎就出現了上圖的服務員先端走了未初始化的food的情況。
鑑於課程的內容,這邊就直接在food類中加一個訊號量,用類似互斥鎖的同步方法(其實生產者消費者問題,應該是廚師製作食物達到上限後,才沉睡,但是本問題是將一個食物反覆操作,相當於上限只能是1),(廚師做完活以後就睡眠並喚醒服務員,服務員做完後也沉睡並喚醒廚師,原本是準備照著這麼做的,結果我用的不是繼承thread,而是繼承Runnable介面,所以似乎不能這麼用),那麼用一個boolean訊號量來做這個同步就好,程式碼和結果是這樣的:

class cook implements Runnable{
    food f;
    static final int count = 50;
    cook(food f){
        this.f = f;
    }
    @Override
    public void run() {
        for (int i = 0; i < count; i++) {
            if(!f.flag) {
                f.l.lock();
                if (i % 2 == 0) {
                    f.setName("手撕雞");
                    f.setTaste("麻辣味");
                    f.setFood();
                } else {
                    f.setName("滷豬腳");
                    f.setTaste("滷煮味");
                    f.setFood();
                }
                f.flag = true;
                f.l.unlock();
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class waiter implements Runnable{
    food f;
    static final int count = 50;
    waiter(food f){
        this.f = f;
    }
    @Override
    public void run() {
        for (int i = 0; i < count; i++) {
            if(f.flag) {
                f.l.lock();
                f.getFood();
                f.flag = false;
                f.l.unlock();
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

其中,在food類里加入了一個flag,這裡覺得太長就不全放上來了,其實程式碼的修改不多。
這個時候執行的結果就比較舒適了:
在這裡插入圖片描述
這就是一個簡單的生產者消費者執行緒同步練習。