• 微信抢红包简单实现(随机分配金额、并发控制)


    package test.concurrent;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;

    public class QiangHongbao {

    public static class Hongbao {
    private Double money;
    private Integer num;

    public Hongbao(Double money, int num) {
    this.money = money;
    this.num = num;
    }

    public Double getMoney() {
    return money;
    }

    public Integer getNum() {
    return num;
    }

    public void setMoney(Double money) {
    this.money = money;
    }

    public void setNum(Integer num) {
    this.num = num;
    }

    }


    /**
    * 如果红包金额和数量用cas修改的化,会产生金额和数量不一致的问题
    * 当前的红包数量可能被其他线程减了1,但是金额没减
    * 分配算法参考:https://www.zhihu.com/question/22625187
    * @return
    */
    public static synchronized double getRandomMoney(Hongbao hongbao) throws RuntimeException {
    if (hongbao.getNum() <= 0) {
    return 0;
    }
    if (hongbao.getNum() == 1) {
    hongbao.setNum(hongbao.getNum() - 1);
    return (double) Math.round(hongbao.getMoney() * 100) / 100;
    }
    double rest = hongbao.getMoney();
    int restNum = hongbao.getNum();
    Random r = new Random();
    double min = 0.01;
    double max = rest / restNum * 2;
    double imoney = r.nextDouble() * max;
    double get = (double) Math.round((imoney <= min ? min : imoney) * 100) / 100;

    hongbao.setNum(hongbao.getNum() - 1);
    hongbao.setMoney(hongbao.getMoney() - get);

    return get;
    }


    public static void main(String[] args) {
    int threadNum = 100;
    double AllMoney = 200.00;
    int packageNum = 20;
    //200块,20个包,100个人抢
    Hongbao hongbao = new Hongbao(AllMoney, packageNum);
    List<Double> collect = new ArrayList<>();
    Random r = new Random();
    Semaphore semaphore = new Semaphore(packageNum);
    for (int i = 0; i < threadNum; i++) {
    Thread th = new Thread(() -> {
    try {
    //模拟网络延迟
    Thread.sleep(r.nextInt(10));
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    try {
    long startTime = System.currentTimeMillis();
    semaphore.tryAcquire(10, TimeUnit.MILLISECONDS);
    if(r.nextInt(threadNum) == 1){
    //随机挂掉一个, 补给其他等待的人
    throw new RuntimeException();
    }
                //获取红包
    Double iGet = getRandomMoney(hongbao);
    collect.add(iGet);
    System.out.println(Thread.currentThread().getName()
    + " i get money " + iGet + " cost " + (System.currentTimeMillis() - startTime) +"ms");
    } catch (Exception e) {
    System.out.println(Thread.currentThread().getName() + " get money failed, cause " + e.getMessage());
    } finally {
    semaphore.release();
    }

    });
    th.start();
    }
         //下面只是为了确认金额无误
    try {
    Thread.sleep(1000L);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    double total = collect.stream().mapToDouble(Double::doubleValue).sum();
    System.out.println("total money is " + total);
    }
    }


    1、我看网上说红包金额和个数扣减用cas,但是我发现2个属性不是整体变动,会存在并发问题。我这里用了悲观锁。
    2、线程控制用了信号量
    欢迎留言
  • 相关阅读:
    170516、ActiveMQ 的安装与使用(单节点)
    170515、mybatis批量操作
    170512、java日志文件log4j.properties配置详解
    170511、Spring IOC和AOP 原理彻底搞懂
    170510、数据库默认隔离级别
    170509、文本编辑器编写的shell脚本在linux下无法执行的解决方法
    170508、忘记jenkins密码或者修改jenkins密码
    170505、MySQL的or/in/union与索引优化
    170504、MongoDB和MySQL对比(译)
    Jenkins Android 打包
  • 原文地址:https://www.cnblogs.com/but999/p/12594411.html
Copyright © 2020-2023  润新知