微信搶紅包簡單實現(隨機分配金額、並發控制)


package test.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class QiangHongbao {

public static class Hongbao {
private Double money;
private Integer num;

public Hongbao(Double money, int num) {
this.money = money;
this.num = num;
}

public Double getMoney() {
return money;
}

public Integer getNum() {
return num;
}

public void setMoney(Double money) {
this.money = money;
}

public void setNum(Integer num) {
this.num = num;
}

}


/**
* 如果紅包金額和數量用cas修改的化,會產生金額和數量不一致的問題
* 當前的紅包數量可能被其他線程減了1,但是金額沒減
* 分配算法參考:https://www.zhihu.com/question/22625187
* @return
*/
public static synchronized double getRandomMoney(Hongbao hongbao) throws RuntimeException {
if (hongbao.getNum() <= 0) {
return 0;
}
if (hongbao.getNum() == 1) {
hongbao.setNum(hongbao.getNum() - 1);
return (double) Math.round(hongbao.getMoney() * 100) / 100;
}
double rest = hongbao.getMoney();
int restNum = hongbao.getNum();
Random r = new Random();
double min = 0.01;
double max = rest / restNum * 2;
double imoney = r.nextDouble() * max;
double get = (double) Math.round((imoney <= min ? min : imoney) * 100) / 100;

hongbao.setNum(hongbao.getNum() - 1);
hongbao.setMoney(hongbao.getMoney() - get);

return get;
}


public static void main(String[] args) {
int threadNum = 100;
double AllMoney = 200.00;
int packageNum = 20;
//200塊,20個包,100個人搶
Hongbao hongbao = new Hongbao(AllMoney, packageNum);
List<Double> collect = new ArrayList<>();
Random r = new Random();
Semaphore semaphore = new Semaphore(packageNum);
for (int i = 0; i < threadNum; i++) {
Thread th = new Thread(() -> {
try {
//模擬網絡延遲
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}

try {
long startTime = System.currentTimeMillis();
semaphore.tryAcquire(10, TimeUnit.MILLISECONDS);
if(r.nextInt(threadNum) == 1){
//隨機掛掉一個, 補給其他等待的人
throw new RuntimeException();
}
            //獲取紅包
Double iGet = getRandomMoney(hongbao);
collect.add(iGet);
System.out.println(Thread.currentThread().getName()
+ " i get money " + iGet + " cost " + (System.currentTimeMillis() - startTime) +"ms");
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " get money failed, cause " + e.getMessage());
} finally {
semaphore.release();
}

});
th.start();
}
     //下面只是為了確認金額無誤
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
double total = collect.stream().mapToDouble(Double::doubleValue).sum();
System.out.println("total money is " + total);
}
}


1、我看網上說紅包金額和個數扣減用cas,但是我發現2個屬性不是整體變動,會存在並發問題。我這里用了悲觀鎖。
2、線程控制用了信號量
歡迎留言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM