实现要点
- 熟悉java多线程应用
- 对抢红包逻辑有所了解。
主要分成三个部分:
1.红包(红包的总金额、每个红包金额、红包数量、是否预先分配)
2.人(每一个线程当做一个人)
3.抢红包(多个人抢红包,返回抢到的金额或者提示没有抢到)
第一种:抢红包时随机生成红包(规定总金额和数量)
红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁
/**
* @author liuzonghua
* @Package thread
* @Description: 红包算法:抢时分配金额,金额波动为0-平均值*2
*服务器压力较大
* @date 2019/2/8 21:30
*/
public class HongBao {
private double total;// 总钱数
private int totalVal; // 总钱数小数化整数
private int count; //红包总数
public HongBao(double total, int count) {
this.totalVal = (int)(total *100);//把总钱数化为整数
this.count = count;
}
public synchronized double getRandomMoney(){
int val;//每个人分配到的金额*100
//当剩下的每一个人只分到一分钱的时候
if(count !=0 && totalVal/count ==1){
val = 1;
totalVal = totalVal -val;
count--;
return val/100.0;
}
if(count <= 0){
val = 0;
}else if(count == 1){
val =totalVal;//如果是最后一个
}else {
int temp; //剩下的金额
while(true) {
// 随机生成当前金额的随机数 [0,totalVal/count-1),尽量平均一点
val = new Random().nextInt(totalVal/count*2);
temp = totalVal - val;
// 判断生成的金额大于0,且剩余的钱数够剩下人平分到0.01元
if(temp*1.0/(count-1) >= 1 && val > 0) {
//System.out.println("生成金额 :" + val + "剩余金额 :" + temp + "剩余人数 :" + (count-1));
break;
}
}
totalVal = totalVal - val;
}
count--;
return val/100.0;
}
}
人: 人在这里用线程代替
public class UserThread implements Runnable{
private HongBao hongBao;
public UserThread(HongBao hongBao) {
this.hongBao = hongBao;
}
@Override
public void run() {
double money = hongBao.getRandomMoney();
if(money == 0) {
System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
}else {
System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
}
}
}
抢红包: 启动多个线程抢红包
public class QiangHongBao {
public static void main(String[] args) {
HongBao hongBao =new HongBao(8.1,8);
UserThread user = new UserThread(hongBao);
for(int i=0;i<10;i++) {
new Thread(user).start();
}
}
}
第二种:提前分配好金额(规定总金额和数量)
红包: 提前分配好金额,金额存在一个集合(队列)抢红包时根据顺序取金额,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁
public class HongBao2 {
private double total;// 总钱数
private int totalVal; // 总钱数小数化整数
private int count; //红包总数
private List<Double> valList;//红包分配集合
public HongBao2(double total, int count) {
this.totalVal = (int)(total *100);//把总钱数化为整数
this.count = count;
this.valList = new ArrayList<Double>();
for(int i=0;i<count;i++) {
int val;//每个人分配到的金额*100
//当剩下的每一个人只分到一分钱的时候
if(count !=0 && totalVal/count ==1){
val = 1;
totalVal = totalVal -val;
valList.add(val/100.0);
}
if(count <= 0){
val = 0;
}else if(count == 1){
val =totalVal;//如果是最后一个
}else {
int temp; //剩下的金额
while(true) {
// 随机生成当前金额的随机数 [0,totalVal/count*2) 金额波动为0-平均值*2
val = new Random().nextInt(totalVal/count*2);
temp = totalVal - val;
// 判断生成的金额大于0,且剩余的钱数够剩下人平分到0.01元
if(temp*1.0/(count-1) >= 1 && val > 0) {
//System.out.println("生成金额 :" + val + "剩余金额 :" + temp + "剩余人数 :" + (count-1));
break;
}
}
totalVal = totalVal - val;
}
valList.add(val/100.0);
}
}
public synchronized double getRandomMoney(){
double money = 0;
if(count > 0){
money =valList.get(count-1)/1.0;
}
count--;
return money;
}
}
人: 人在这里用线程代替
public class UserThread2 implements Runnable{
private HongBao2 hongBao2;
public UserThread2(HongBao2 hongBao2) {
this.hongBao2 = hongBao2;
}
@Override
public void run() {
double money = hongBao2.getRandomMoney();
if(money == 0) {
System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
}else {
System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
}
}
}
抢红包: 启动多个线程抢红包
public class QiangHongBao {
public static void main(String[] args) {
HongBao2 hongBao2 =new HongBao2(8.1,8);
UserThread2 user2 = new UserThread2(hongBao2);
for(int i=0;i<10;i++) {
new Thread(user2).start();
}
}
}
第三种:知道总金额,不确定红包数,总金额发完即止
为实现其效果:
- 方案一:伪代码实现,算法对于用户是黑盒的。可以套用前面两种方法,规定总金额和红包数,以红包数发完即止代替总金额发完即止。
虽然实现过程不同,但实现效果完全一样。 - 方案二:真代码实现,每一个线程获取一红包,但总金额小于某个值(平均值或者平均值*2)后面线程返回没抢到提示。
红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁
public class HongBao3 {
private double total;// 总钱数
private int totalVal; // 总钱数小数化整数
private int ceilVal;//红包最高额度*100 取整
public HongBao3(double total,int ceilVal) {
this.totalVal = (int)(total *100);//把总钱数化为整数
this.ceilVal = ceilVal*100;
}
public synchronized double getRandomMoney(){
int val;//每个人分配到的金额*100
// 随机生成当前金额的随机数 [0,ceilVal)
val = new Random().nextInt(ceilVal);
// 判断生成的金额等于0,便发0.01元
if(val == 0){
val = 1;
}else if(val > totalVal){
val = totalVal;
}
totalVal = totalVal - val;
return val/100.0;
}
}
人: 人在这里用线程代替
public class UserThread3 implements Runnable{
private HongBao3 hongBao3;
public UserThread3(HongBao3 hongBao3) {
this.hongBao3 = hongBao3;
}
@Override
public void run() {
double money = hongBao3.getRandomMoney();
if(money == 0) {
System.out.println(Thread.currentThread().getName() + "不好意思,您手慢了!");
}else {
System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
}
}
}
抢红包: 启动多个线程抢红包
public class QiangHongBao {
public static void main(String[] args) {
HongBao3 hongBao3 =new HongBao3(29,6);
UserThread3 user3 = new UserThread3(hongBao3);
for(int i=0;i<10;i++) {
new Thread(user3).start();
}
}
}
第四种:总金额不限,红包限额,红包数不限,有多少人抢发多少个
红包: 在抢红包的过程中剩余金额会不断减少,要保证不能两个人同时抢,需要加上悲观锁,同一时间只可以一个人抢,加synchronized锁
public class HongBao4 {
private int ceilVal;//红包最高额度*100 取整
public HongBao4(double ceil) {
this.ceilVal = (int) ceil*100;
}
public synchronized double getRandomMoney(){
int val;//每个人分配到的金额*100
val = new Random().nextInt(ceilVal);
return val/100.0;
}
}
人: 人在这里用线程代替
public class UserThread4 implements Runnable{
private HongBao4 hongBao4;
public UserThread4(HongBao4 hongBao4) {
this.hongBao4 = hongBao4;
}
@Override
public void run() {
double money = hongBao4.getRandomMoney();
System.out.println(Thread.currentThread().getName() + "抢到 " + money + "元");
}
}
抢红包: 启动多个线程抢红包
public class QiangHongBao {
public static void main(String[] args) {
HongBao4 hongBao4 =new HongBao4(29);
UserThread4 user4 = new UserThread4(hongBao4);
for(int i=0;i<10;i++) {
new Thread(user4).start();
}
}
}
其他要素
微信抢红包当然不仅仅是这样。在互联网时代高并发随处可见,抢红包也是一个高并的典型事件。主要借助mq(如:RabbitMQ)把抢红包的请求队列化,根据顺序获取红包金额(其中可以设置各种限制条件如:每人多少个,隔多少秒后才可以继续抢)。
通过时间复杂度看程序
微信抢红包中其实可以分成两大部分,1.用户抢红包算法分配金额,2.微信金额到账。
其中微信金额到账需要调用微信接口。接口中有大量的各种安全、信息相关的算法。时间复杂度相比第一部分高多了。所有抢红包程序通常因此崩溃。
市面通常解决办法:(类最终一致性)先完成第一部分保存数据并提示用户已获得红包金额,在 计算机有富余能力后才开始调用微信金额到账。