需求:
抢红包也用到了多线程。
假设:100块,分成了3个包,现在有5个人去抢。
其中,红包是共享数据。
5个人是5条线程。
打印结果如下:
XXX抢到了XXX元
XXX抢到了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 + "元");
}
}
}
}
在写测试类的时候教你一招
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元。
写完后发现,虽然逻辑对了,数据好像也没有什么太大问题,但是跟实际生活好像有一点不一样,在实际生活中,应该保留小数点后面两位,我要进行精确计算怎么办?
因此我们还需要将这个代码去改一改,改成精确运算就行了,即使用 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 + "元");
}
}
}
}