黑马程序员技术交流社区

标题: 多线程之随机卖包子案例的正确解法 [打印本页]

作者: 李春林    时间: 2016-10-9 09:56
标题: 多线程之随机卖包子案例的正确解法
[Java] 纯文本查看 复制代码
package com.heima.moreThread多线程;

import java.util.Random;

public class Test02 {

        /**
         * /*3. 某包子店铺生意火爆,现开5个窗口模拟售卖100个包子,每次每个窗口随机卖出1-5个包子,
                卖完最后一个包子后提示”包子已售完”(必须全部卖出),程序结束.(要求使用Thread类和Runnable两种方式去实现)
                 * */
        public static void main(String[] args) {
                MyBaoZi mb = new MyBaoZi();
                Thread t1 = new Thread(mb,"窗口1");
                Thread t2 = new Thread(mb,"窗口2");
                Thread t3 = new Thread(mb,"窗口3");
                Thread t4 = new Thread(mb,"窗口4");
                Thread t5 = new Thread(mb,"窗口5");
                t1.start();
                t2.start();
                t3.start();
                t4.start();
                t5.start();
        }

}

class MyBaoZi implements Runnable {
        //总共的包子数,这是共享数据
        private int baozi = 100;
        //设置标记,为输出一次"包子已经卖完了"做准备
        private boolean flag = true;
        //创建随机数对象
        Random r = new Random();
        @Override
        public void run() {
                //获取随机数,相当于获取随机卖出的包子个数
                int i = r.nextInt(5) + 1;
                while (true) {
                        synchronized (MyBaoZi.class) {
                                //如果剩余的包子数等于0或者是小于0,就直接跳出
                                if(baozi <= 0) {
                                        break;                                                                                                //跳出
                                }
                                try {
                                        Thread.sleep(20);                                                        //防止卖得太快,让程序睡一会
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                                //这一步的判断的目的是在于只剩下一个包子的时候,怎么样才能卖出去,
                                //当获取到的随机数是在包子的剩余范围的时候,交易才能成功
                                if (baozi >= i) {
                                        //获取到卖出之后的剩余包子数
                                        baozi = baozi - i;
                                        //输出卖出去包子的信息
                                        System.out.println(Thread.currentThread().getName() + "...出售了" + i  + "个包子,还剩下" + baozi + "个包子");
                                }
                        }
                        //必须保证是卖最后一个包子的线程执行这一段代码
                        synchronized(MyBaoZi.class) {
                                if (flag && baozi <= 0) {
                                        System.out.println("包子已经卖完了");
                                        flag = false;
                                }
                        }
                }
        }
       
}


作者: 读书可以当饭吃    时间: 2016-10-9 14:10
从买票,卖包包到卖包子,多线程真会玩
作者: 读书可以当饭吃    时间: 2016-10-9 14:24
你们有没有想过,当前面卖了99个包子,剩最后一个的时候,随机数出了一个4...
作者: wangsenaho1649    时间: 2016-10-9 16:19
精神可嘉啊 。。
作者: 此间的少年    时间: 2016-10-9 16:55
感觉线程好难
作者: 李春林    时间: 2016-10-9 17:42
此间的少年 发表于 2016-10-9 16:55
感觉线程好难

多敲吧,敲多了,就有手感了
作者: 李春林    时间: 2016-10-9 17:44
读书可以当饭吃 发表于 2016-10-9 14:24
你们有没有想过,当前面卖了99个包子,剩最后一个的时候,随机数出了一个4... ...

所以,我在代码中才会做最后一步的判断,如果包子的剩余数不是大于或者等于随机数,根本就卖不出去,只能重新摇随机数
作者: 李春林    时间: 2016-10-9 17:45
读书可以当饭吃 发表于 2016-10-9 14:24
你们有没有想过,当前面卖了99个包子,剩最后一个的时候,随机数出了一个4... ...

所以,我在代码中才会做最后一步的判断,如果包子的剩余数不是大于或者等于随机数,根本就卖不出去,只能重新摇随机数
作者: 李春林    时间: 2016-10-10 09:12
多线程真的很重要,昨天机试都遇到了
作者: HackSon    时间: 2016-10-10 10:34
友情提示,你的随机数应该在循环内获取,你现在在循环外,所以每次的的包子数都是不变的,根本不会重新获取
作者: 李春林    时间: 2016-10-10 19:44
HackSon 发表于 2016-10-10 10:34
友情提示,你的随机数应该在循环内获取,你现在在循环外,所以每次的的包子数都是不变的,根本不会重新获取 ...

对啊,5条线程,每条线程一次只获取一个随机数,如果不符合要求,那就直接跳出咯,只有获取到的随机数在剩余包子的范围内,交易才能成立

作者: 万物皆数    时间: 2016-10-10 22:23
出问题一般在边界上
作者: yu244934256    时间: 2016-10-11 00:38
你怎么搞了2个同步代码块
作者: 李春林    时间: 2016-10-11 01:20
yu244934256 发表于 2016-10-11 00:38
你怎么搞了2个同步代码块

请看注解

作者: HackSon    时间: 2016-10-11 10:13
李春林 发表于 2016-10-10 19:44
对啊,5条线程,每条线程一次只获取一个随机数,如果不符合要求,那就直接跳出咯,只有获取到的随机数在 ...

你这样会出现卖不出包子的情况,比如:最后剩下一个包子,可是每个线程的随机数都大于1,因为你获取随机数是在循环外面,所以一次循环不符合后再次循环随机数还是不变的,这样包子数一直为1,无法跳出循环,也无法卖出。
作者: wjhsyy    时间: 2016-10-11 10:47
多线程一直比较迷糊,看完感觉思路清晰多了
作者: wangkai426    时间: 2016-10-11 10:51
好难的样子,谢谢楼主分享
作者: 李春林    时间: 2016-10-11 17:37
HackSon 发表于 2016-10-11 10:13
你这样会出现卖不出包子的情况,比如:最后剩下一个包子,可是每个线程的随机数都大于1,因为你获取随机 ...

你确定你运行了我的程序了吗??

作者: 李春林    时间: 2016-10-11 17:45
HackSon 发表于 2016-10-11 10:13
你这样会出现卖不出包子的情况,比如:最后剩下一个包子,可是每个线程的随机数都大于1,因为你获取随机 ...

多线程是什么,你都还没有完全搞懂,每条线程,一次只能获取一个随机数,当随机数在包子剩余范围内时,这条线程才能继续执行下去,不然,线程直接结束,换下一条线程。如果按照你说的,把获取随机数也放在循环里面,那只能是一条线程一直在跑

作者: 蓝光四号    时间: 2016-10-11 17:58
mark,,,

作者: 梁古叔广    时间: 2016-10-11 19:00
这是我的卖包子,献丑了,希望各位指正;
在循环中加循环是为了保证在最后所剩包子数小于顾客所需数时,让该顾客在该窗口重新买少于或等于所剩包子数量的包子,也就是说该顾客在该窗口说买5个,剩余包子没那么多,我得告诉他没那么多,让他少买点,而不是说剩余不足5个我就不在该窗口卖给给顾客了;
增加第二个随机数是保证顾客说的第二个数一定可以买到也就是顾客第一次说要买5个,销售员说没有5个只有3个,顾客必须买少于3个,因为顾客再说买4个或者5个的话会被认为是找茬的,可能要引起口角,不好.
package com.java;


public class 测试 {



        public static void main(String[] args) {
                //创建5条线程代表5个窗口
                Thread t1 = new BaoZi();
                Thread t2 = new BaoZi();
                Thread t3 = new BaoZi();
                Thread t4 = new BaoZi();
                Thread t5 = new BaoZi();
                //命名
                t1.setName("1号窗口");
                t2.setName("2号窗口");
                t3.setName("3号窗口");
                t4.setName("4号窗口");
                t5.setName("5号窗口");
                //开启线程,开卖
                t1.start();
                t2.start();
                t3.start();
                t4.start();
                t5.start();

        }

}

class BaoZi extends Thread {
        //100个包子
        private static int baoZi = 100;

        @Override
        public void run() {
                //此处循环是为了卖完包子
                while (true) {
                        synchronized (BaoZi.class) {
                                //卖完为止
                                if (baoZi<= 0) {

                                        break;
                                }
                                //此处循环是为了保证最后包子数少于顾客要购买的量(随机数),该顾客在该窗口重新随机数再买
                                while (true) {
                                        //当前窗口当前顾客要购买的包子数
                                        int i = (int) (Math.random() * 5) + 1;
                                        //所剩包子小于顾客想要购买的数量时(告诉该顾客包子数不够,你要买等于或者少于剩余数的包子)
                                        if (i > baoZi) {
                                                //让该顾客在该窗口重新买,而且保证重新买的数量在包子剩余数范围内(第二次随机必须买到)
                                                int t = (int) (Math.random() * baoZi) + 1;
                                                //卖出包子
                                                baoZi -= t;
                                                //判断卖出包子是否有剩余,无剩余则提示卖完并结束本次销售
                                                if (baoZi == 0) {
                                                        System.out.println(this.getName() + "本次卖出" + t
                                                                        + "个包子,一共卖了" + (100 - baoZi) + "个包子,卖完了");
                                                        break;
                                                        //如果卖了之后还有剩余则打印本次销售的信息以及剩余的包子数,并结束本次销售
                                                } else {
                                                        System.out.println(this.getName() + "本次卖出" + t
                                                                        + "个包子,一共卖了" + (100 - baoZi) + "个包子,还剩"
                                                                        + baoZi + "个包子");
                                                        break;
                                                }
                                                //当包子剩余数大于顾客所需购买量时,直接销售不需要顾客再重新随机数
                                        } else {
                                                //卖出包子,并判断是否卖完,打印当次的销售情况和剩余包子数
                                                baoZi -= i;
                                               
                                                if (baoZi == 0) {
                                                        System.out.println(this.getName() + "本次卖出" + i
                                                                        + "个包子,一共卖了" + (100 - baoZi) + "个包子,卖完了");
                                                        break;
                                                } else {
                                                        System.out.println(this.getName() + "本次卖出" + i
                                                                        + "个包子,一共卖了" + (100 - baoZi) + "个包子,还剩"
                                                                        + baoZi + "个包子");
                                                        break;
                                                }
                                        }
                                }
                        }
                }

        }
}

作者: HackSon    时间: 2016-10-11 21:08
李春林 发表于 2016-10-11 17:45
多线程是什么,你都还没有完全搞懂,每条线程,一次只能获取一个随机数,当随机数在包子剩余范围内时,这 ...

“开启五个线程卖包子,每个线程每次随机获取1-5个包子”,这句话你理解好了没有?意思是每个线程每次循环都随机获取一个随机数作为包子卖出的个数,你这个程序一进去的时候获取一个随机数,之后一直循环,随机数没有重新获取,一直不变,如果你的随机数比包子剩余的数量都大的时候,无法卖出包子,也无法跳出循环,懂吗?麻烦你回去调试好了再说话好吗,遇到别人指出你程序的问题你第一反应不是好好检查程序,而是语言攻击,这样就能表现出你很牛吗?论坛是大家交流学习的地方,不是你装逼的地方,OK?
作者: 梁古叔广    时间: 2016-10-12 18:56
本帖最后由 梁古叔广 于 2016-10-12 19:00 编辑

你这个如果最后随机数不是碰巧等于包子数,绝对直接死循环,该线程一直拥有执行权,一直循环,因为你随机数大于最后的包子数的时候,while循环你没有设置出口,死循环
作者: guozesen    时间: 2016-10-12 21:04
广叔的代码可行,然, 代码还是臃肿了点,,,可以改的简单点的,...
作者: 老爹很帅    时间: 2016-10-12 22:18
厉害厉害!
作者: 回根的落叶    时间: 2016-10-13 00:35
HackSon 发表于 2016-10-11 21:08
“开启五个线程卖包子,每个线程每次随机获取1-5个包子”,这句话你理解好了没有?意思是每个线程每次循 ...

@HackSon 正解,获取随机数必须放进循环内,至于是放在同步代码块外面还是同步代码块里面都是可以的,个人看法最好放同步代码块里面。。{:2_32:}
作者: HackSon    时间: 2016-10-13 15:43
回根的落叶 发表于 2016-10-13 00:35
@HackSon 正解,获取随机数必须放进循环内,至于是放在同步代码块外面还是同步代码块里面都是可以的,个 ...

学技术就应该虚心点,如果别人一个善意的提示他都不能接受的话,那我只能呵呵了
作者: HackSon    时间: 2016-10-13 15:46
梁古叔广 发表于 2016-10-12 18:56
你这个如果最后随机数不是碰巧等于包子数,绝对直接死循环,该线程一直拥有执行权,一直循环,因为你随机数大于 ...

英雄所见略同{:2_31:}
作者: 梁古叔广    时间: 2016-10-15 00:27
HackSon 发表于 2016-10-13 15:46
英雄所见略同

我去,英雄这个就说大了,我只是凑巧知道这一点点而已,以后还得跟各位多学习





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2