【代碼練習6】利用多線程生產消費問題實現熊喫蜂蜜問題

熊喫蜂蜜問題:
2只熊,100只蜜蜂,
蜜蜂每次生成的蜂蜜量爲1,罐子的容量是50,當罐子的蜂蜜量達到20時,熊就喫光。
罐子使用單例設計模式實現。

import java.util.LinkedList;
import java.util.List;

/*
熊喫蜂蜜問題:
        2只熊,100只蜜蜂,
        蜜蜂每次生成的蜂蜜量爲1,罐子的容量是50,當罐子的蜂蜜量達到20時,熊就喫光。
        罐子使用單例設計模式實現。
 */

public class BearEatHoney {
    public static void main(String[] args) {
        Pot pot = new Pot();
        Bear b1 = new Bear("熊大",pot);
        Bear b2 = new Bear("熊二",pot);

        b1.start();
        b2.start();
        //開啓100只蜜蜂的線程
        for(int i = 1;i <= 100;i++){
            new Bee(i + "號蜜蜂",pot).start();
        }

    }
    //罐子類:單例設計模式(懶漢模式)
    static class Pot {
        //擁有一個類類型的成員變量。
        private Pot pot = null;
        //罐子集合
        private List<Integer> list = null;
        //罐子的容積
        private int Max = 50;

        //私有化構造器
        private Pot(){
            list = new LinkedList<Integer>();
        }

        //通過相應的公有方法獲得對象
        public Pot getPot(){
            if(pot!= null){
                return pot;
            }
            synchronized (this){
                if(pot == null){
                    pot = new Pot();
                }
                return pot;
            }
        }

        //構造罐子添加蜂蜜的方法,爲保證線程安全,採用synchronized同步代碼塊對add方法上鎖
        public synchronized void add(int n,String beeName) {
                while (list.size() == Max) {
                    try {
                        this.wait();//當罐子灌滿時,調用該方法的蜜蜂線程進入等待列表
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                list.add(n);
            /*如果將打印方法放在蜜蜂線程裏,由於在多線程的狀態下,在控制檯打印的順序並不能真正反映處理器處理的順序,
            *爲了更好的查看運行結果,所以我暫且選擇放在了同步代碼塊裏。
            *但其實在實際應用時,爲提高程序運行速度,應該儘量減少同步代碼塊裏的執行動作*/
                System.out.println(beeName + "加了一滴,罐子裏有" + list.size() + "滴蜂蜜");
                this.notifyAll();
        }

        //構造罐子的移除方法,同樣爲了保證線程安全,採用synchronized同步代碼塊對clean方法上鎖
        public synchronized void clean(String bearName) {
                while (list.size() < 20) {
                    try {
                        this.wait();//當罐子裏蜂蜜的量小於20時,調用該方法的線程進入等待列表
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                list.clear();
                this.notifyAll();
            System.out.println("\r\n" + "Y(● ̄(エ) ̄●)Y " + bearName + "把蜂蜜喫光了。。。。" + "\r\n");
        }
    }

    //蜂蜜線程
    static class Bee extends Thread{
        private Pot pot;
        private String beeName;

        public Bee() {}
        public Bee(String beeName,Pot pot){
            this.beeName = beeName;
            this.pot = pot;
        }

        public void run() {
            for (;;){
                try {
                    pot.add(1,beeName);
                     /*
                *蜜蜂線程太多,總有更大的概率搶到罐子,所以同等條件下熊多數時候是在罐子灌滿時才能喫到蜂蜜,
                *爲了讓熊儘可能在多種情況下都能喫到,我們就讓蜜蜂每次放完蜂蜜後時候睡一下
                */
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //熊線程
    static class Bear extends Thread{
        private Pot pot;
        private String bearName;

        public Bear(){}
        public Bear(String bearName,Pot pot){
            this.bearName = bearName;
            this.pot = pot;
        }
        public void run(){

            for (;;){
                    pot.clean(bearName);
            }
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章