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、线程控制用了信号量
欢迎留言