抢红包问题

抢红包问题

许多人看到这个问题的第一反应是怎么分配红包,用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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值