面试题目:一个抽奖池设计(含代码)

本文探讨了如何设计一个高并发抽奖系统,要求包括限制每个面额的抽奖次数及总金额不超过5万元。通过使用AtomicInteger的CAS操作解决了并发问题,保证性能。提供了抽奖函数的实现,并通过测试类模拟30000次抽奖,耗时27毫秒,验证了设计的正确性和效率。
摘要由CSDN通过智能技术生成

题目

如何设计一个抽奖池,总抽奖金额5w元,里面有1元的,2元的,5元的面额等等。每个面额的有指定的次数限制,比如1元的10000次,2元的20000次,5元的2000次。
要求:

  1. 每个面额的次数不能超
  2. 总抽奖金额不能超
  3. 性能不能太差,比如不能设置一个全局锁,至少是用户级别的锁

设计思想

  1. 该场景主要需要解决高并发问题,
  2. 1元, 2元, 3元奖券, 以及金额池采用抽奖之前先冻结必要的奖券数和金额的方式, 之后抽奖后用不到的奖券和金额再释放掉.
  3. 最初考虑bitmap来着, 但是bitmap的占用和释放以及遍历每每都是麻烦, 最后采用AtomicInteger的CAS的方式解决并发量和多线程问题.

代码

先编写抽奖相关的函数, 至于抽奖池以及奖券的占用和释放先写为抽象函数, 之后慢慢实现

package cn.cpf.test;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;

/**
 * <b>Description : </b>
 *
 * @author CPF
 * @date 2020/12/19 20:27
 **/
@Slf4j
public abstract class AbstractLotteryMachine {
   

    private final Random random = new Random();
    // 抽奖概率, 1元, 2元, 5元的概率比重为 22:44:4
    private final int[] probability = new int[]{
   22, 44, 4};

    /**
     * 抽奖方法
     *
     * @return 抽奖金额(1: 1元, 2: 2元, 5: 5元, 0: 没奖了)
     */
    public int lottery() {
   
        boolean et1 = false;
        boolean et2 = false;
        boolean et5 = false;
        int occupyMoney = 0;
        int money = 0;
        try {
   
            occupyMoney = occupyMoneyTotal();
            if (occupyMoney == 0) {
   
                return money;
            }
            assert occupyMoney > 0;
            et1 = occupyMoney1();
            et2 = occupyMoney2();
            et5 = occupyMoney5();
            money = randomMoney(et1, et2, et5, occupyMoney);
            assert money <= occupyMoney;
            return money;
        } catch (RuntimeException e){
   
            log.error("et1: {}, et2: {}, et3:{}, occ:{}, money: {}", et1, et2, et5, occupyMoney, money);
            throw e;
        } finally {
   
            if (et1 && money != 1) {
   
                releaseMoney1();
            }
            if (et2 && money != 2) {
   
                releaseMoney2();
            }
            if (et5 && money != 5) {
   
                releaseMoney5();
            }
            assert occupyMoney >= money;
            if (occupyMoney > 0) {
   
                final int releaseMoney = occupyMoney - money;
                releaseMoneyTotal(releaseMoney);
            }
        }
    }

    /**
     * 10000张1元 + 20000张2元 刚好等于 50000, 也就是说最优的情况是刚好抽奖抽了10000张1元 和 20000张2元,
     * 5元的奖券抽中的次数越少越节约成本, 1元, 2元的券之间概率调整没什么意义
     *
     * @param et1         1元奖券是否存在
     * @param et2         2元奖券是否存在
     * @param et5         5元奖券是否存在
     * @param occupyMoney 占据金额
     * @return 抽取的奖券 0:没有合适的奖券了, 1: 1元, 2: 2元, 5: 5元
     */
    protected int randomMoney(boolean et1, boole
  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逆光影者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值