抢红包问题

抢红包问题

许多人看到这个问题的第一反应是怎么分配红包,用0~1随机数或是X-Y(0,1)正态分布随机数,力图使红包分配更合理。但忽略了一个非常重要的字眼–“抢”,所以这个问题主要考虑的是并发编程。

获得正态分布随机数方式

1.Random中得nextGaussian()方法,是X-Y(0,1)标准正态分布随机数
2 独立同分布的中心极限定理
3.Box–Muller算法

代码

import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Random;


public class RedEnvelopes {
	private double MAX=1;
	private double MIN=0.4;
	private int people; 
	private int stay; 
	private static final Random rand = new Random();
	private int balance; 
	private int total;
	private double[] data;
	public RedEnvelopes(double total,int people) {
		this.people=people;
		this.stay=this.people;
		/*
		将total乘以100取整避免浮点运算带来的误差
		*/
		this.total=(int)(total*100);
		this.balance=this.total;
		createData();
		System.out.println("RedEnvelopes data:"+Arrays.toString(data) );
		double sum=0;
		for(int i=0;i<data.length;i++) {
			sum+=data[i];
		}
		System.out.println("data sum :"+sum);
	}
    public void createData() {
    	data=new double[people];
    	for(int i=people-1;i>=0;i--) {
    		if(i==0) {
    			data[i]=1.0*balance/100;
    			return;
    		}
    		int  mean=balance/(i+1);
    		double tmp=0;
    		do {
    			tmp=Math.abs(rand.nextGaussian());
    		}while(tmp<MIN||tmp>MAX);
    		int ret=(int) (tmp*mean);
    		balance-=ret;
    		data[i]=1.0*ret/100;
    	}
    }
    //返回实际金额
	public   double get() throws Exception {
		if(stay>0) {
			--stay;
			return data[stay];
		}
		else
			throw new Exception("Error");
	}
}

测试

	public static void main(String[] args) {
		RedEnvelopes red=new RedEnvelopes(100,10);
	}

输出

RedEnvelopes data:[24.27, 11.07, 14.54, 14.49, 9.31, 7.12, 5.35, 4.71, 4.5, 4.64]
data sum :100.0
(sum的值由于浮点运算带来的误差可能不为100,但data的和必为100)

添加两个线程

		public static void main(String[] args) {
		RedEnvelopes red=new RedEnvelopes(100,10);
		Integer i=10;
		Runnable r1=new Runnable() {
			public void run() {
				for(int i=0;i<5;i++) {
					try {
						System.out.println("Runnable1:"+red.get());
					}catch (Exception e) {
						e.printStackTrace();
					}
					
				}
				
			}
		};
		Runnable r2=new Runnable() {
			public void run() {
				for(int i=0;i<5;i++) {
					try {
						System.out.println("Runnable2:"+red.get());
					}catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		};
		Thread t1 =new Thread(r1);
		t1.start();
		Thread t2 = new Thread(r2);
		t2.start();
	}

输出

浮点运算带来的误差显而易见,所以转换为整型是必要的。

在这里插入图片描述

两个线程同时去取钱看样子没问题。

放大时延

	public   double get() throws Exception {
		if(stay>0) {
			--stay;
			Thread.sleep(1000);
			return data[stay];
		}
		else
			throw new Exception("Error");
	}

输出

在这里插入图片描述
时延变大后问题凸显了出来。

解决办法

用 synchronized修饰get()方法,避免并发访问

	public  synchronized double get() throws Exception {
		if(stay>0) {
			--stay;
			Thread.sleep(1000);
			return data[stay];
		}
		else
			throw new Exception("Error");
	}

输出

在这里插入图片描述
对于抢红包问题,显然这样解决方式是比较正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FastAdmin 是一款基于ThinkPHP5框架开发的快速后台开发框架,它提供了快速搭建后台系统的功能,但是在实现红包功能方面,需要结合其他的开发技术进行实现。 在具体实现红包功能时,可以通过以下步骤: 1. 创建红包表(red_packet)和红包记录表(grab_record)。 红包表包括红包ID、红包金额、剩余金额和红包类型等字段,红包记录表包括红包记录ID、红包ID、用户ID和到的金额等字段。 2. 编写红包接口。 通过FastAdmin框架提供的快速开发功能,编写一个红包的接口。在接口中,首先需要判定红包是否还有剩余金额,若没有则返回红包失败信息,若有剩余金额,则先将红包表中的剩余金额减去到的金额,并更新红包表。然后将红包记录插入红包记录表中,记录红包的用户ID和到的金额等信息。最后返回红包成功的信息。 3. 前端页面展示。 在前端页面中,可以通过调用红包接口来实现红包的功能。用户点击红包按钮时,前端发送请求到后台接口,后台进行相应的处理并返回结果给前端页面展示。 需要注意的是,在实现红包功能时,还需要考虑并发控制的问题,以防止多个用户同时对同一个红包进行夺,导致金额错误或其他问题的发生。可以通过使用锁机制或者分布式锁等方式进行并发控制,确保每次只有一个用户可以成功红包。 综上所述,通过FastAdmin框架,我们可以结合其他技术手段来实现红包功能,从而为用户提供更好的使用体验和互动性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值