java多线程实现抢红包(介绍四种红包分配算法实现方式)

实现要点

  1. 熟悉java多线程应用
  2. 对抢红包逻辑有所了解。
    主要分成三个部分:
    1.红包(红包的总金额、每个红包金额、红包数量、是否预先分配)
    2.人(每一个线程当做一个人)
    3.抢红包(多个人抢红包,返回抢到的金额或者提示没有抢到)

第一种:抢红包时随机生成红包(规定总金额和数量)

红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁

/**
 * @author liuzonghua
 * @Package thread
 * @Description: 红包算法:抢时分配金额,金额波动为0-平均值*2
 *服务器压力较大
 * @date 2019/2/8 21:30
 */
public class HongBao {
    private double total;// 总钱数
    private int totalVal; // 总钱数小数化整数
    private int count; //红包总数

    public HongBao(double total, int count) {
        this.totalVal = (int)(total *100);//把总钱数化为整数
        this.count = count;
    }

    public synchronized double getRandomMoney(){
        int val;//每个人分配到的金额*100
        //当剩下的每一个人只分到一分钱的时候
        if(count !=0 && totalVal/count ==1){
            val = 1;
            totalVal = totalVal -val;
            count--;
            return val/100.0;
        }

        if(count <= 0){
            val = 0;
        }else if(count == 1){
            val =totalVal;//如果是最后一个
        }else {
            int temp; //剩下的金额
            while(true) {
                // 随机生成当前金额的随机数 [0,totalVal/count-1),尽量平均一点
                val = new Random().nextInt(totalVal/count*2);
                temp = totalVal - val;
                // 判断生成的金额大于0,且剩余的钱数够剩下人平分到0.01元

                if(temp*1.0/(count-1) >= 1 && val > 0) {
                    //System.out.println("生成金额 :" + val + "剩余金额 :" + temp + "剩余人数 :" + (count-1));
                    break;
                }
            }
            totalVal = totalVal - val;
        }
        count--;
        return val/100.0;
    }
}

人: 人在这里用线程代替

public class UserThread implements Runnable{
    private HongBao hongBao;

    public UserThread(HongBao hongBao) {
        this.hongBao = hongBao;
    }

    @Override
    public void run() {
        double money = hongBao.getRandomMoney();
        if(money == 0) {
            System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
        }else {
            System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
        }

    }
}

抢红包: 启动多个线程抢红包

public class QiangHongBao {
    public static void main(String[] args) {
        HongBao hongBao =new HongBao(8.1,8);
        UserThread user = new UserThread(hongBao);
        for(int i=0;i<10;i++) {
            new Thread(user).start();
        }
}
}

第二种:提前分配好金额(规定总金额和数量)

红包: 提前分配好金额,金额存在一个集合(队列)抢红包时根据顺序取金额,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁

public class HongBao2 {
    private double total;// 总钱数
    private int totalVal; // 总钱数小数化整数
    private int count; //红包总数
    private List<Double> valList;//红包分配集合

    public HongBao2(double total, int count) {
        this.totalVal = (int)(total *100);//把总钱数化为整数
        this.count = count;
        this.valList = new ArrayList<Double>();
        for(int i=0;i<count;i++) {
            int val;//每个人分配到的金额*100
            //当剩下的每一个人只分到一分钱的时候
            if(count !=0 && totalVal/count ==1){
                val = 1;
                totalVal = totalVal -val;

                valList.add(val/100.0);
            }

            if(count <= 0){
                val = 0;
            }else if(count == 1){
                val =totalVal;//如果是最后一个
            }else {
                int temp; //剩下的金额
                while(true) {
                    // 随机生成当前金额的随机数 [0,totalVal/count*2) 金额波动为0-平均值*2
                    val = new Random().nextInt(totalVal/count*2);
                    temp = totalVal - val;
                    // 判断生成的金额大于0,且剩余的钱数够剩下人平分到0.01元

                    if(temp*1.0/(count-1) >= 1 && val > 0) {
                        //System.out.println("生成金额 :" + val + "剩余金额 :" + temp + "剩余人数 :" + (count-1));
                        break;
                    }
                }
                totalVal = totalVal - val;
            }
            valList.add(val/100.0);
        }
    }

    public synchronized double getRandomMoney(){
        double money = 0;
        if(count > 0){
            money =valList.get(count-1)/1.0;
        }
        count--;
        return money;
    }
}

人: 人在这里用线程代替

public class UserThread2 implements Runnable{
    private HongBao2 hongBao2;

    public UserThread2(HongBao2 hongBao2) {
        this.hongBao2 = hongBao2;
    }

    @Override
    public void run() {
        double money = hongBao2.getRandomMoney();
        if(money == 0) {
            System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
        }else {
            System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
        }

    }
}

抢红包: 启动多个线程抢红包

public class QiangHongBao {
    public static void main(String[] args) {

        HongBao2 hongBao2 =new HongBao2(8.1,8);

        UserThread2 user2 = new UserThread2(hongBao2);
        for(int i=0;i<10;i++) {
            new Thread(user2).start();
        }
    }
}

第三种:知道总金额,不确定红包数,总金额发完即止

为实现其效果:

  • 方案一:伪代码实现,算法对于用户是黑盒的。可以套用前面两种方法,规定总金额和红包数,以红包数发完即止代替总金额发完即止。
    虽然实现过程不同,但实现效果完全一样。
  • 方案二:真代码实现,每一个线程获取一红包,但总金额小于某个值(平均值或者平均值*2)后面线程返回没抢到提示。

红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁

public class HongBao3 {
    private double total;// 总钱数
    private int totalVal; // 总钱数小数化整数
    private int ceilVal;//红包最高额度*100 取整


    public HongBao3(double total,int ceilVal) {
        this.totalVal = (int)(total *100);//把总钱数化为整数
        this.ceilVal = ceilVal*100;
    }

    public synchronized double getRandomMoney(){
        int val;//每个人分配到的金额*100
        // 随机生成当前金额的随机数 [0,ceilVal)
        val = new Random().nextInt(ceilVal);
        // 判断生成的金额等于0,便发0.01元
        if(val == 0){
           val = 1;
        }else if(val > totalVal){
            val = totalVal;
        }
        totalVal = totalVal - val;
        return val/100.0;
    }
}

人: 人在这里用线程代替

public class UserThread3 implements Runnable{
    private HongBao3 hongBao3;

    public UserThread3(HongBao3 hongBao3) {
        this.hongBao3 = hongBao3;
    }

    @Override
    public void run() {
        double money = hongBao3.getRandomMoney();
        if(money == 0) {
            System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
        }else {
            System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
        }

    }
}

抢红包: 启动多个线程抢红包

public class QiangHongBao {
    public static void main(String[] args) {
    
        HongBao3 hongBao3 =new HongBao3(29,6);

        UserThread3 user3 = new UserThread3(hongBao3);
        for(int i=0;i<10;i++) {
            new Thread(user3).start();
        }
    }
}

第四种:总金额不限,红包限额,红包数不限,有多少人抢发多少个

红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁

public class HongBao4 {
    private int ceilVal;//红包最高额度*100 取整
   

    public HongBao4(double ceil) {
        this.ceilVal = (int) ceil*100;
      
    }

    public synchronized double getRandomMoney(){
        int val;//每个人分配到的金额*100
        val = new Random().nextInt(ceilVal);
        return val/100.0;
    }
}

人: 人在这里用线程代替

public class UserThread4 implements Runnable{
    private HongBao4 hongBao4;

    public UserThread4(HongBao4 hongBao4) {
        this.hongBao4 = hongBao4;
    }

    @Override
    public void run() {
        double money = hongBao4.getRandomMoney();
        System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
    }
}

抢红包: 启动多个线程抢红包

public class QiangHongBao {
    public static void main(String[] args) {
        HongBao4 hongBao4 =new HongBao4(29);

        UserThread4 user4 = new UserThread4(hongBao4);
        for(int i=0;i<10;i++) {
            new Thread(user4).start();
        }
    }
}

其他要素

微信抢红包当然不仅仅是这样。在互联网时代高并发随处可见,抢红包也是一个高并的典型事件。主要借助mq(如:RabbitMQ)把抢红包的请求队列化,根据顺序获取红包金额(其中可以设置各种限制条件如:每人多少个,隔多少秒后才可以继续抢)。

通过时间复杂度看程序

微信抢红包中其实可以分成两大部分,1.用户抢红包算法分配金额,2.微信金额到账。
其中微信金额到账需要调用微信接口。接口中有大量的各种安全、信息相关的算法。时间复杂度相比第一部分高多了。所有抢红包程序通常因此崩溃。
市面通常解决办法:(类最终一致性)先完成第一部分保存数据并提示用户已获得红包金额,在 计算机有富余能力后才开始调用微信金额到账。
(如:春晚红包24小时内到账,游戏活动装备7天内到账)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值