zoukankan      html  css  js  c++  java
  • 微信抢红包简单实现(随机分配金额、并发控制)

    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、线程控制用了信号量
    欢迎留言
  • 相关阅读:
    01《软件工程思想》读后感01
    寒假小软件开发记录04--添加字体
    寒假小软件开发记录03--选择图片
    fiddler2 中文乱码问题
    一个很好的软件 fiddler4
    关于svn的安装问题
    ZF-关于海南的增删改需求
    (转)收集:Hibernate中常见问题 No row with the given identifier exists问题的原因及解决
    关于sqlserver还原不了数据库的原因
    OraclePLSQL Developer报“动态执行表不可访问,本会话的自动统计被禁止”的解决方案
  • 原文地址:https://www.cnblogs.com/but999/p/12594411.html
Copyright © 2011-2022 走看看