【代码练习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);
            }
        }
    }

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