当前位置:   article > 正文

java抢红包实现

java抢红包实现

目录

总体思想

红包算法

个人看法

控制器

测试效果

优化点

lua脚本的好处


之前看过一篇文章介绍抢红包的,现在自己搞一哈

总体思想

说下大概思路,有一种是抢一个红包,那么下一个拿到的是总数-抢到的钱数,然后再去随机

另一种是先把钱拆分好,然后再按人头去分,这一篇主要是这种方法

拆分完之后放到redis list,然后通过leftpop进行输出

红包算法

参考网上的,然后个人再新增一个函数,输出拆分红包的list:

  1. package com.example.demo.entity;
  2. import java.math.BigDecimal;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. public class Red {
  6. private int remain;//金额,单位厘
  7. private int count;//个数
  8. private Precision precision;//精度
  9. private int max;//上限,单位厘
  10. private int min;//下限,单位厘
  11. private int[] redPool;
  12. private int index;
  13. public static void main(String[] args) {
  14. getRedPackage(10000 * 100, 5000, 280, 1 * 10);
  15. }
  16. /**
  17. * 拆分红包
  18. * @param money 总钱数 (分)
  19. * @param count 红包个数
  20. * @param max 最大的红包(分)
  21. * @param min 最小红包(分)
  22. * @return
  23. */
  24. public static List<Double> getRedPackage(int money, int count, int max, int min) {
  25. Red red = Red.newInstance(money * Precision.FEN.getPre(), count, Precision.FEN, max * Precision.FEN.getPre(), min * Precision.FEN.getPre());
  26. List<Double> RedPackageList = new LinkedList<>();
  27. BigDecimal bigDecimal = new BigDecimal(money);
  28. BigDecimal bigDecimal1 = new BigDecimal(100);
  29. System.out.println("总钱数为:" + bigDecimal.divide(bigDecimal1) + "元");
  30. for (int i = 0; i < count; i++) {
  31. int money1 = red.getRed();
  32. BigDecimal bigDecimal2 = new BigDecimal(money1);
  33. BigDecimal bigDecimal3 = new BigDecimal(1000);
  34. double lastMoney = bigDecimal2.divide(bigDecimal3).doubleValue();
  35. RedPackageList.add(lastMoney);
  36. System.out.println("拆分红包:" + lastMoney + "元");
  37. }
  38. return RedPackageList;
  39. }
  40. public int getRed() {
  41. return index < count ? redPool[index++] : 0;
  42. }
  43. public static Red newInstance(int money, int count, Precision precision, int max, int min) {
  44. Red red = new Red(money, count, precision, max, min);
  45. String msg;
  46. if ("".equals(msg = red.validate())) return red;
  47. else throw new RuntimeException(msg);
  48. }
  49. private Red(int money, int count, Precision precision, int max, int min) {
  50. this.remain = money;
  51. this.count = count;
  52. this.precision = precision;
  53. this.max = max;
  54. this.min = min;
  55. init();
  56. }
  57. private void init() {
  58. redPool = new int[count];
  59. int remain_ = remain;
  60. for (int i = 0; i < count - 1; i++) {
  61. int max = getRealMax(remain_, count - i);
  62. int min = getRealMin(remain_, count - i);
  63. int money = ((int) (Math.random() * (max - min + precision.getPre())) + min)
  64. / precision.getPre() * precision.getPre();//[min, realMax]
  65. remain_ -= money;
  66. redPool[i] = money;
  67. }
  68. redPool[count - 1] = remain_;
  69. randomPool();
  70. }
  71. private void randomPool() {
  72. for (int i = 0; i < count; i++) {
  73. int index = (int) (Math.random() * count);
  74. int temp = redPool[i];
  75. redPool[i] = redPool[index];
  76. redPool[index] = temp;
  77. }
  78. }
  79. private int getRealMax(int remain, int count) {
  80. int calMax = remain - ((count - 1) * min);
  81. return Math.min(calMax, max);
  82. }
  83. private int getRealMin(int remain, int count) {
  84. int calMin = remain - ((count - 1) * max);
  85. return Math.max(calMin, min);
  86. }
  87. public int getRemain() {
  88. return remain;
  89. }
  90. public void setRemain(int remain) {
  91. this.remain = remain;
  92. }
  93. public int getCount() {
  94. return count;
  95. }
  96. public void setCount(int count) {
  97. this.count = count;
  98. }
  99. public int getMax() {
  100. return max;
  101. }
  102. public void setMax(int max) {
  103. this.max = max;
  104. }
  105. public int getMin() {
  106. return min;
  107. }
  108. public void setMin(int min) {
  109. this.min = min;
  110. }
  111. public Precision getPrecision() {
  112. return precision;
  113. }
  114. public void setPrecision(Precision precision) {
  115. this.precision = precision;
  116. }
  117. private String validate() {
  118. String msg = "";
  119. if (remain <= 0) {
  120. msg = "余额不能为0";
  121. } else if (remain % precision.getPre() != 0) {
  122. msg = "余额的精度不对";
  123. } else if (count <= 0) {
  124. msg = "红包个数必须为正数";
  125. } else if (max % precision.getPre() != 0) {
  126. msg = "上限的精度不对";
  127. } else if (max <= min) {
  128. msg = "上限必须大于下限";
  129. } else if (min % precision.getPre() != 0) {
  130. msg = "下限的精度不对";
  131. } else if (min <= 0) {
  132. msg = "下限必须大于0";
  133. } else if (getRealMax(remain, count) < getRealMin(remain, count)) {
  134. msg = "上下限设置错误";
  135. }
  136. return msg;
  137. }
  138. }
  139. enum Precision {
  140. LI(1),
  141. FEN(10),
  142. JIAO(100),
  143. YUAN(1000);
  144. private int pre;
  145. private Precision(int pre) {
  146. this.pre = pre;
  147. }
  148. public int getPre() {
  149. return pre;
  150. }
  151. }

个人看法

个人评论一下,在下面这个函数中,你需要将max设计很有水平才能实现拆分效果会比较好,不然就一堆0.01

public static List<Double> getRedPackage(int money, int count, int max, int min)

 

控制器

  1. package com.example.demo.Controller;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. import javax.annotation.Resource;
  6. import java.util.List;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. import java.util.concurrent.CountDownLatch;
  9. import static com.example.demo.entity.Red.getRedPackage;
  10. @RestController
  11. public class RedController {
  12. @Resource
  13. private RedisTemplate redisTemplate;
  14. private static String key = "redpackage";
  15. @RequestMapping("/red")
  16. public void getRed() throws InterruptedException {
  17. List<Double> list = getRedPackage(10000 * 100, 5000, 280, 1 * 10);
  18. for(double d:list){
  19. redisTemplate.opsForList().leftPush(key, d);
  20. }
  21. ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
  22. long time = System.nanoTime();
  23. CountDownLatch countDownLatch = new CountDownLatch(100);
  24. for (int i = 0; i < 5000; i++) {
  25. new Thread(new Runnable() {
  26. @Override
  27. public void run() {
  28. countDownLatch.countDown();
  29. /*if (concurrentHashMap.get(Thread.currentThread().getName()) != null) {
  30. System.out.println("该线程已经抢过");
  31. } else {
  32. concurrentHashMap.put(Thread.currentThread().getName(), 1);
  33. }*/
  34. System.out.println(Thread.currentThread().getName()+"抢到红包:"+redisTemplate.opsForList().leftPop(key));
  35. }
  36. }).start();
  37. }
  38. countDownLatch.await();
  39. long time1 = System.nanoTime();
  40. System.out.println("耗时" + (time1 - time));
  41. }
  42. }

 

测试效果

大概一秒可以完成1W个抢红包,跟原来看到的文章有所出入,因为另一位作者是使用redis+lua实现的,包括防止用户重复抢,使用hset,我这里使用redisTemplate好像没有hset这个功能,所以这里考虑使用Map来保存,实际开发如果不是高并发的话,可以保存到数据库中,进行查询用户是否已经领取红包。不可能都保存到map中,一个红包一个map,占用内存巨大的。

 

优化点

除了上面说的使用hset保存用户抢到的红包,去重。之外可以考虑将用户的请求全部放到一个队列,然后再批量抢红包,使用lua脚本实现。

 

lua脚本的好处

1)通过类似机器语言,相当执行一条语句,来保证线程安全

2)一次性执行完,减少redis反复请求,减少网络请求

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/849625
推荐阅读
相关标签
  

闽ICP备14008679号