抢红包问题

抢红包问题

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

输出

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

猜你喜欢

转载自blog.csdn.net/qq_39464369/article/details/89435308