【Java】多线程综合练习:抢红包

需求:
	抢红包也用到了多线程。
	假设:100块,分成了3个包,现在有5个人去抢。
	其中,红包是共享数据。
	5个人是5条线程。
	打印结果如下:
		  XXX抢到了XXXXXX抢到了XXX

四个套路,但又由于这里抢红包每个人只能抢一次,因此第一步循环就不用写了

1. 循环
2. 同步代码块(之后可以改写为同步方法或者lock锁,都行)
3. 判断共享数据是否到了末尾(到了末尾),建议先写到了末尾的情况,因为到了末尾更简单
4. 判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)

PS:需要随机带小数,可以使用 Random类nextDouble(double bound),如果你传递 100,它就会在100中随机,随机的时候是带小数的,只不过这个方法在JDK17的时候才能用,低版本用不了。

public class MyThread extends Thread {
    //共享数据
    //100块,分成了3个包
    static double money = 100;
    static int count = 3;

    //最小的中奖金额,能是0吗?不能,最小也是最小的中奖金额
    //又由于最小的中奖金额是不会发生变化的,因此用final修饰
    //用final修饰后,它就变成常量了,常量的名字要大写
    static final double MIN = 0.01;

    @Override
    public void run() {
        //同步代码块
        synchronized (MyThread.class) {
            if (count == 0) {
                //判断,共享数据是否到了末尾(已经到末尾)
                System.out.println(getName() + "没有抢到红包!");
            } else {
                //判断,共享数据是否到了末尾(没有到末尾),就抽奖
                //但是抽奖的时候不能直接随机,因为随机到最后一次,就不要再随机了,第三次就是剩余的钱,因此随机前应该对count进行判断
                //定义一个变量,表示中奖的金额
                double prize = 0;
                if (count == 1) {
                    //表示此时是最后一个红包
                    //就无需随机,剩余所有的钱都是中奖金额
                    prize = money;
                } else {
                    //表示第一次,第二次(随机)
                    Random r = new Random();
                    //nextDouble()中随机的范围不能直接写money,下面举个例子你就懂了
                    //假设现在是100 元 分成了 3个包
                    //第一个红包的最大金额应该为:99.98
                    //计算方式:100 - (3-1) * 0.01(最小值),这样计算出来的才是随机的范围
                    double bounds = money - (count - 1) * MIN;
                    prize = r.nextDouble(bounds);
                    if (prize < MIN) {
                        //如果小于最小的金额,此时就将你强制变成最小的金额。
                        prize = MIN;
                    }
                }
                //从money当中,去掉当前中奖的金额
                money = money - prize;
                //红包的个数-1
                count--;
                //本次红包的信息进行打印
                System.out.println(getName() + "抢到了" + prize + "元");
            }
        }
    }
}

在写测试类的时候教你一招

kfmyb-35gxy
public static void main(String[] args) {
    //创建线程的对象
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();

    //给线程设置名字
    t1.setName("小A");
    t2.setName("小QQ");
    t3.setName("小哈哈");
    t4.setName("小诗诗");
    t5.setName("小丹丹");

    //启动线程
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
}

运行程序,这就是我们想要的结果,三个人抢到了,两个人没有抢到,而且三个人的总金额是100元。

image-20240507084737529

写完后发现,虽然逻辑对了,数据好像也没有什么太大问题,但是跟实际生活好像有一点不一样,在实际生活中,应该保留小数点后面两位,我要进行精确计算怎么办?

因此我们还需要将这个代码去改一改,改成精确运算就行了,即使用 BigDecimal

只不过 BigDecimal 在用起来的时候非常难受,非常不方便,它里面加减乘除所有的全部都是使用方法来实现的。

在操作完后,还需要对抽奖结果进行一个四舍五入的设置,并且小数点保留两位。

//设置抽中红包,小数点保留两位,四舍五入
prize = prize.setScale(2,RoundingMode.HALF_UP);

完整代码

public class MyThread extends Thread {
    //总金额
    static BigDecimal money = BigDecimal.valueOf(100.0);
    //个数
    static int count = 3;
    //最小抽奖金额
    static final BigDecimal MIN = BigDecimal.valueOf(0.01);

    @Override
    public void run() {
        synchronized (MyThread.class) {
            if (count == 0) {
                System.out.println(getName() + "没有抢到红包!");
            } else {
                //中奖金额
                BigDecimal prize;
                if (count == 1) {
                    prize = money;
                } else {
                    //获取抽奖范围
                    double bounds = money.subtract(BigDecimal.valueOf(count - 1).multiply(MIN)).doubleValue();
                    Random r = new Random();
                    //抽奖金额
                    prize = BigDecimal.valueOf(r.nextDouble(bounds));
                }
                //设置抽中红包,小数点保留两位,四舍五入
                prize = prize.setScale(2, RoundingMode.HALF_UP);
                //在总金额中去掉对应的钱
                money = money.subtract(prize);
                //红包少了一个
                count--;
                //输出红包信息
                System.out.println(getName() + "抽中了" + prize + "元");
            }
        }
    }
}
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值