Java線程(三):線程協作-生產者/消費者模式 – JAVA編程語言程序開發技術文章

假設有這樣一種情況,有一個盤子,盤子裡隻能放一個雞蛋,A線程專門往盤子裡放雞蛋,如果盤子裡有雞蛋,則一直等到盤子裡沒雞蛋,B線程專門從盤子裡取雞蛋,如果盤子裡沒雞蛋,則一直等到盤子裡有雞蛋。這裡盤子是一個互斥區,每次放雞蛋是互斥的,每次取雞蛋也是互斥的,A線程放雞蛋,如果這時B線程要取雞蛋,由於A沒有釋放鎖,B線程處於等待狀態,進入阻塞隊列,放雞蛋之後,要通知B線程取雞蛋,B線程進入就緒隊列,反過來,B線程取雞蛋,如果A線程要放雞蛋,由於B線程沒有釋放鎖,A線程處於等待狀態,進入阻塞隊列,取雞蛋之後,要通知A線程放雞蛋,A線程進入就緒隊列。我們希望當盤子裡有雞蛋時,A線程阻塞,B線程就緒,盤子裡沒雞蛋時,A線程就緒,B線程阻塞,代碼如下:
[java] 
import java.util.ArrayList; 
import java.util.List; 
/** 定義一個盤子類,可以放雞蛋和取雞蛋 */ 
public class Plate { 
    /** 裝雞蛋的盤子 */ 
    List<Object> eggs = new ArrayList<Object>(); 
    /** 取雞蛋 */ 
    public synchronized Object getEgg() { 
        while (eggs.size() == 0) { 
            try { 
                wait(); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
        Object egg = eggs.get(0); 
        eggs.clear();// 清空盤子 
        notify();// 喚醒阻塞隊列的某線程到就緒隊列 
        System.out.println("拿到雞蛋"); 
        return egg; 
    } 
    /** 放雞蛋 */ 
    public synchronized void putEgg(Object egg) { 
        while (eggs.size() > 0) { 
            try { 
                wait(); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
        eggs.add(egg);// 往盤子裡放雞蛋 
        notify();// 喚醒阻塞隊列的某線程到就緒隊列 
        System.out.println("放入雞蛋"); 
    } 
    static class AddThread extends Thread { 
        private Plate plate; 
        private Object egg = new Object(); 
        public AddThread(Plate plate) { 
            this.plate = plate; 
        } 
        public void run() { 
            plate.putEgg(egg); 
        } 
    } 
    static class GetThread extends Thread { 
        private Plate plate; 
        public GetThread(Plate plate) { 
            this.plate = plate; 
        } 
        public void run() { 
            plate.getEgg(); 
        } 
    } 
    public static void main(String args[]) { 
        Plate plate = new Plate(); 
        for(int i = 0; i < 10; i++) { 
            new Thread(new AddThread(plate)).start(); 
            new Thread(new GetThread(plate)).start(); 
        } 
    } 

        輸出結果:
[java] 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
放入雞蛋 
拿到雞蛋 
        8 l程序開始,A線程判斷盤子是否為空,放入一個雞蛋,並且喚醒在阻塞隊列的一個線程,阻塞隊列為空;假設CPU又調度瞭一個A線程,盤子非空,執行等待,這個A線程進入阻塞隊列;然後一個B線程執行,盤子非空,取走雞蛋,並喚醒阻塞隊列的A線程,A線程進入就緒隊列,此時就緒隊列就一個A線程,馬上執行,放入雞蛋;如果再來A線程重復第一步,在來B線程重復第二步,整個過程就是生產者(A線程)生產雞蛋,消費者(B線程)消費雞蛋。
        前段時間看瞭張孝祥老師線程的視頻,講述瞭一個其學員的面試題,也是線程通信的,在此也分享一下。
        題目:子線程循環10次,主線程循環100次,如此循環100次,好像是空中網的筆試題。
[java] 
public class ThreadTest2 { 
    public static void main(String[] args) { 
        final Business business = new Business(); 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                threadExecute(business, "sub"); 
            } 
        }).start(); 
        threadExecute(business, "main"); 
    }    
    public static void threadExecute(Business business, String threadType) { 
        for(int i = 0; i < 100; i++) { 
            try { 
                if("main".equals(threadType)) { 
                    business.main(i); 
                } else { 
                    business.sub(i); 
                } 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

class Business { 
    private boolean bool = true; 
    public synchronized void main(int loop) throws InterruptedException { 
        while(bool) { 
            this.wait(); 
        } 
        for(int i = 0; i < 100; i++) { 
            System.out.println("main thread seq of " + i + ", loop of " + loop); 
        } 
        bool = true; 
        this.notify(); 
    }    
    public synchronized void sub(int loop) throws InterruptedException { 
        while(!bool) { 
            this.wait(); 
        } 
        for(int i = 0; i < 10; i++) { 
            System.out.println("sub thread seq of " + i + ", loop of " + loop); 
        } 
        bool = false; 
        this.notify(); 
    } 

        大傢註意到沒有,在調用wait方法時,都是用while判斷條件的,而不是if,在wait方法說明中,也推薦使用while,因為在某些特定的情況下,線程有可能被假喚醒,使用while會循環檢測更穩妥。

發佈留言