多线程-模拟抢红包,抽奖池
目录
场景1:抢红包
场景2:模拟抽奖池
场景1:抢红包
100块,分成了3个包,现在有5个人去抢,
其中,红包是共享数据。
5个人是5条线程。如何用多线程方式去实现?
public class MyThread extends Thread {
private static Integer totalNum = 3; //红包总数量
private static Double totalAmount = 100.0; //红包总金额
private static final Double MIN = 0.01;
@Override
public void run() {
synchronized (MyThread.class) {
if (totalNum == 0) {
System.out.println(getName() + "没有抢到红包");
} else {
DecimalFormat df = new DecimalFormat("#.00");
double redAmount;
if (totalNum == 1) {
//最后剩余得金额就是最后一个红包
String result = df.format(totalAmount);
double formattedNum = Double.parseDouble(result); // 将字符串转换为 double 值
redAmount = formattedNum;
} else {
double bounds = totalAmount - (totalNum - 1) * MIN;
Random random = new Random();
double v = random.nextDouble() * bounds + MIN; //随机红包数量
String result = df.format(v);
double formattedNum = Double.parseDouble(result); // 将字符串转换为 double 值
if (v < MIN) {
v = MIN;
}
redAmount = formattedNum;
}
totalAmount = totalAmount -redAmount;
totalNum--;
System.out.println(getName() + "抢到红包!"+redAmount+"元");
}
}
}
}
- totalNum 表示红包的总数量,totalAmount 表示红包的总金额,MIN 表示每个红包的最小金额。
- 在 run() 方法中,使用 synchronized 关键字实现了对共享资源的互斥访问,保证了多个线程同时抢红包时,不会出现数据冲突问题。
- 如果 totalNum 为 0,则输出 "没有抢到红包";否则,进入抢红包逻辑。
- 如果 totalNum 等于 1,那么最后一个红包的金额就是剩余的 totalAmount;否则,根据 totalNum 和 totalAmount 计算出当前红包的金额。
- 在计算随机红包金额时,首先根据 totalNum 和 totalAmount 计算出当前红包金额的上限 bounds,然后使用 Java 的 Random 类生成一个 0 到 bounds 之间的随机数 v,再根据 MIN 确保每个红包的最小金额不低于 MIN。
- 最后更新 totalAmount 和 totalNum 的值,并输出抢到红包的线程名和对应的红包金额。
main方法:
public class Main {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
MyThread myThread4 = new MyThread();
MyThread myThread5 = new MyThread();
myThread1.setName("小红");
myThread2.setName("小明");
myThread3.setName("小李");
myThread4.setName("小高");
myThread5.setName("小闫");
myThread1.start();
myThread2.start();
myThread3.start();
myThread4.start();
myThread5.start();
}
}
结果打印:
场景2:模拟抽奖池
有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为[10, 34, 56, 78, 54, 800, 900, 23, 45, 32, 1, 2, 34]
创建两个抽奖箱(线程),随机从抽奖池中获取奖项打印在控制台上
public class Draw extends Thread {
private String boxName;
private static Boolean isOpen = false;
private static final List<Integer> list = new ArrayList<>();
Draw(String boxName) {
this.boxName = boxName;
}
public static synchronized int box() {
if (!isOpen) {
createList();
isOpen = true;
}
int index = new Random().nextInt(list.size());
Integer integer = list.get(index);
list.remove(index);
return integer;
}
static List<Integer> createList() {
list.add(10);
list.add(34);
list.add(56);
list.add(78);
list.add(54);
list.add(800);
list.add(900);
list.add(23);
list.add(45);
list.add(32);
list.add(1);
list.add(2);
list.add(34);
return list;
}
@Override
public void run() {
while (true) {
int box = box();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(boxName + "抽出:" + box);
if (list.size()<=0){
break;
}
}
}
}
1.定义一个boxName的字符串变量,表示抽奖箱的名称。此外,还有一个名为 "isOpen" 的静态布尔类型变量,用于跟踪奖励箱是否已经被打开,创建一个"list" 的静态整数类型列表,存储了一些奖励数据。
2.该类有一个构造函数,接受一个字符串参数,用于设置抽奖箱的名称。
3.定义一个”box“方法用于实际执行抽奖操作。在这个方法中,首先检查 "isOpen" 变量的状态,如果奖励箱尚未打开,则调用名为 "createList" 的方法,将奖励添加到 "list" 中,并将 "isOpen" 设置为 "true"。保证只有一个线程能初始化数据。
4.使用 "Random" 类生成一个随机整数,并从 "list" 中取出该索引处的整数,最后将该整数从 "list" 中删除,并将其作为结果返回。
5.定义了一个名为 "createList" 的静态方法,用于添加一些奖励到 "list" 中。在这个方法中,将一些整数添加到 "list" 中,并将其返回。
6.覆盖了 "Thread" 类的 "run" 方法。在这个方法中,使用 "box" 方法从奖励列表中获取一个奖励,并将其输出到控制台上。然后,线程休眠一秒钟,然后继续进行下一个循环,直到 "list" 中没有更多奖励为止。
public class Main {
public static void main(String[] args) {
Draw draw1 = new Draw("抽奖箱1");
Draw draw2 = new Draw("抽奖箱2");
Thread thread1 = new Thread(draw1);
Thread thread2 = new Thread(draw2);
thread1.start();
thread2.start();
}
}
结果: