赞
踩
目录
之前看过一篇文章介绍抢红包的,现在自己搞一哈
说下大概思路,有一种是抢一个红包,那么下一个拿到的是总数-抢到的钱数,然后再去随机
另一种是先把钱拆分好,然后再按人头去分,这一篇主要是这种方法
拆分完之后放到redis list,然后通过leftpop进行输出
参考网上的,然后个人再新增一个函数,输出拆分红包的list:
- package com.example.demo.entity;
-
- import java.math.BigDecimal;
- import java.util.LinkedList;
- import java.util.List;
-
- public class Red {
- private int remain;//金额,单位厘
- private int count;//个数
- private Precision precision;//精度
- private int max;//上限,单位厘
- private int min;//下限,单位厘
- private int[] redPool;
- private int index;
-
- public static void main(String[] args) {
- getRedPackage(10000 * 100, 5000, 280, 1 * 10);
- }
-
- /**
- * 拆分红包
- * @param money 总钱数 (分)
- * @param count 红包个数
- * @param max 最大的红包(分)
- * @param min 最小红包(分)
- * @return
- */
- public static List<Double> getRedPackage(int money, int count, int max, int min) {
- Red red = Red.newInstance(money * Precision.FEN.getPre(), count, Precision.FEN, max * Precision.FEN.getPre(), min * Precision.FEN.getPre());
- List<Double> RedPackageList = new LinkedList<>();
- BigDecimal bigDecimal = new BigDecimal(money);
- BigDecimal bigDecimal1 = new BigDecimal(100);
- System.out.println("总钱数为:" + bigDecimal.divide(bigDecimal1) + "元");
- for (int i = 0; i < count; i++) {
- int money1 = red.getRed();
- BigDecimal bigDecimal2 = new BigDecimal(money1);
- BigDecimal bigDecimal3 = new BigDecimal(1000);
- double lastMoney = bigDecimal2.divide(bigDecimal3).doubleValue();
- RedPackageList.add(lastMoney);
- System.out.println("拆分红包:" + lastMoney + "元");
- }
- return RedPackageList;
- }
-
- public int getRed() {
- return index < count ? redPool[index++] : 0;
- }
-
- public static Red newInstance(int money, int count, Precision precision, int max, int min) {
- Red red = new Red(money, count, precision, max, min);
- String msg;
- if ("".equals(msg = red.validate())) return red;
- else throw new RuntimeException(msg);
- }
-
- private Red(int money, int count, Precision precision, int max, int min) {
- this.remain = money;
- this.count = count;
- this.precision = precision;
- this.max = max;
- this.min = min;
- init();
- }
-
- private void init() {
- redPool = new int[count];
- int remain_ = remain;
- for (int i = 0; i < count - 1; i++) {
- int max = getRealMax(remain_, count - i);
- int min = getRealMin(remain_, count - i);
- int money = ((int) (Math.random() * (max - min + precision.getPre())) + min)
- / precision.getPre() * precision.getPre();//[min, realMax]
- remain_ -= money;
- redPool[i] = money;
- }
- redPool[count - 1] = remain_;
- randomPool();
- }
-
- private void randomPool() {
- for (int i = 0; i < count; i++) {
- int index = (int) (Math.random() * count);
- int temp = redPool[i];
- redPool[i] = redPool[index];
- redPool[index] = temp;
- }
- }
-
- private int getRealMax(int remain, int count) {
- int calMax = remain - ((count - 1) * min);
- return Math.min(calMax, max);
- }
-
- private int getRealMin(int remain, int count) {
- int calMin = remain - ((count - 1) * max);
- return Math.max(calMin, min);
- }
-
- public int getRemain() {
- return remain;
- }
-
- public void setRemain(int remain) {
- this.remain = remain;
- }
-
- public int getCount() {
- return count;
- }
-
- public void setCount(int count) {
- this.count = count;
- }
-
- public int getMax() {
- return max;
- }
-
- public void setMax(int max) {
- this.max = max;
- }
-
- public int getMin() {
- return min;
- }
-
- public void setMin(int min) {
- this.min = min;
- }
-
- public Precision getPrecision() {
- return precision;
- }
-
- public void setPrecision(Precision precision) {
- this.precision = precision;
- }
-
- private String validate() {
- String msg = "";
- if (remain <= 0) {
- msg = "余额不能为0";
- } else if (remain % precision.getPre() != 0) {
- msg = "余额的精度不对";
- } else if (count <= 0) {
- msg = "红包个数必须为正数";
- } else if (max % precision.getPre() != 0) {
- msg = "上限的精度不对";
- } else if (max <= min) {
- msg = "上限必须大于下限";
- } else if (min % precision.getPre() != 0) {
- msg = "下限的精度不对";
- } else if (min <= 0) {
- msg = "下限必须大于0";
- } else if (getRealMax(remain, count) < getRealMin(remain, count)) {
- msg = "上下限设置错误";
- }
- return msg;
- }
-
- }
-
- enum Precision {
- LI(1),
- FEN(10),
- JIAO(100),
- YUAN(1000);
-
- private int pre;
-
- private Precision(int pre) {
- this.pre = pre;
- }
-
- public int getPre() {
- return pre;
- }
- }
个人评论一下,在下面这个函数中,你需要将max设计很有水平才能实现拆分效果会比较好,不然就一堆0.01
public static List<Double> getRedPackage(int money, int count, int max, int min)
- package com.example.demo.Controller;
-
-
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.annotation.Resource;
- import java.util.List;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.CountDownLatch;
-
- import static com.example.demo.entity.Red.getRedPackage;
-
- @RestController
- public class RedController {
-
- @Resource
- private RedisTemplate redisTemplate;
-
- private static String key = "redpackage";
-
- @RequestMapping("/red")
- public void getRed() throws InterruptedException {
- List<Double> list = getRedPackage(10000 * 100, 5000, 280, 1 * 10);
- for(double d:list){
- redisTemplate.opsForList().leftPush(key, d);
- }
-
- ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
- long time = System.nanoTime();
- CountDownLatch countDownLatch = new CountDownLatch(100);
- for (int i = 0; i < 5000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- countDownLatch.countDown();
- /*if (concurrentHashMap.get(Thread.currentThread().getName()) != null) {
- System.out.println("该线程已经抢过");
- } else {
- concurrentHashMap.put(Thread.currentThread().getName(), 1);
- }*/
- System.out.println(Thread.currentThread().getName()+"抢到红包:"+redisTemplate.opsForList().leftPop(key));
- }
- }).start();
- }
- countDownLatch.await();
- long time1 = System.nanoTime();
- System.out.println("耗时" + (time1 - time));
- }
-
- }
大概一秒可以完成1W个抢红包,跟原来看到的文章有所出入,因为另一位作者是使用redis+lua实现的,包括防止用户重复抢,使用hset,我这里使用redisTemplate好像没有hset这个功能,所以这里考虑使用Map来保存,实际开发如果不是高并发的话,可以保存到数据库中,进行查询用户是否已经领取红包。不可能都保存到map中,一个红包一个map,占用内存巨大的。
除了上面说的使用hset保存用户抢到的红包,去重。之外可以考虑将用户的请求全部放到一个队列,然后再批量抢红包,使用lua脚本实现。
1)通过类似机器语言,相当执行一条语句,来保证线程安全
2)一次性执行完,减少redis反复请求,减少网络请求
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。