以下实现仅个人练习,仅供参考,不足之处请多多留言指教,感激不尽。
抢红包有以下信息需要考虑:
基本逻辑实现:
public class GradCommon {
private int redPacket;
private int redPacketNumber;
private final int redPacketSize;
private final int lastRedPacketSize;
public GradCommon(int redPacket,int redPacketNumber) {
this.redPacketNumber = redPacketNumber;
this.redPacketSize = redPacket/redPacketNumber;
this.lastRedPacketSize = redPacketSize + redPacket % redPacketNumber;
}
public int grad(){
int currentRedPacket = 0;
if (redPacketNumber>0){
redPacketNumber--;
currentRedPacket = redPacketSize;
if (redPacketNumber == 1){
currentRedPacket = lastRedPacketSize;
}
System.out.println(Thread.currentThread().getName() + " 抢到红包" + currentRedPacket + "分,奖励一下!");
}else {
System.out.println(Thread.currentThread().getName() + " 未抢到,感觉错过了一个亿!");
}
return currentRedPacket;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入红包总金额(分):");
int redPacket = scanner.nextInt();
System.out.println("请输入红包总数:");
int redPacketNumber = scanner.nextInt();
System.out.println("请输入抢红包人数");
int people = scanner.nextInt();
GradCommon grad = new GradCommon(redPacket,redPacketNumber);
for (int i = 0; i < people; i++) {
grad.grad(); // 注意此位置问题
}
}
以上代码逻辑问题:
每个人抢红包的动作作为一个独立的线程,所以以多个线程方式抢红包,并且使用CountDownLatch
实现同时开抢效果:
public class GradThread implements Runnable{
private int redPacketNumber;
private final int redPacketSize;
private final int lastRedPacketSize;
private final CountDownLatch countDownLatch;
public GradThread(int redPacket, int redPacketNumber,CountDownLatch countDownLatch) {
this.redPacketNumber = redPacketNumber;
this.countDownLatch = countDownLatch;
this.redPacketSize = redPacket / redPacketNumber;
this.lastRedPacketSize = this.redPacketSize + redPacket % redPacketNumber;
}
@Override
public void run() {
try {
countDownLatch.await(); // 争取同时开始竞价
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (redPacketNumber>0){
redPacketNumber--;
int currentRedPacket = redPacketSize;
if (redPacketNumber == 1){
currentRedPacket = lastRedPacketSize;
}
System.out.println(Thread.currentThread().getName() + " 抢到红包" + currentRedPacket + "分,奖励一下!");
}else {
System.out.println(Thread.currentThread().getName() + " 未抢到,感觉错过了一个亿!");
}
}
}
public static void main(String[] args) {
// ...同上
CountDownLatch countDownLatch = new CountDownLatch(1);
GradThread gradThread = new GradThread(redPacket,redPacketNumber,countDownLatch);
for (int i = 0; i < people; i++) {
new Thread(gradThread).start();
}
countDownLatch.countDown();// 将值降为0,代表同时启动countDownLatch.await()的后续逻辑
}
问题:
增加同步锁synchronized
实现抢红包代码块锁定,避免红包超出
public class GradThread implements Runnable{
// ...其他同上
// 增加一个对象锁
private final Object lock = new Object();
@Override
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (lock){ // 加锁避免超出
if (redPacketNumber>0){
redPacketNumber--;
int currentRedPacket = redPacketSize;
if (redPacketNumber == 1){
currentRedPacket = lastRedPacketSize;
}
System.out.println(Thread.currentThread().getName() + " 抢到红包" + currentRedPacket + "分,奖励一下!");
}else {
System.out.println(Thread.currentThread().getName() + " 未抢到,感觉错过了一个亿!");
}
}
}
}
随机红包即每个人抢到的红包并不是固定的,是随机下发的。
public class RandomGradThread implements Runnable
private final Random random = new Random();
private int redPacketNumber; // 红包数量
private int redPacket; // 红包总额
private int gradRedPacketNumber; // 已抢红包数量
private int gradRedPacket;// 已抢红包总额
private final int minRedPacket = 1; // 在红包数量有效的情况下,每个红包最少有一个,不能为0
private final CountDownLatch countDownLatch;
public RandomGradRedPacket(int redPacket,int redPacketNumber,CountDownLatch countDownLatch) {
this.redPacket = redPacket;
this.redPacketNumber = redPacketNumber;
this.countDownLatch = countDownLatch;
System.out.println("。。。随机红包逻辑。。。");
}
@Override
public void run() {
try {
countDownLatch.await(); // 争取同时开始竞价
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
grad();
}
// 抢红包逻辑
private synchronized void grad() {
if (redPacketNumber == 0){
System.out.println(Thread.currentThread().getName() + " 未抢到,感觉错过了一个亿!");
return;
}
int currentRedPacket = gradCurRedPacket();
// assert currentRedPacket > 0:"红包大小不应该为0!";
if (currentRedPacket == 0){
throw new RuntimeException("红包大小不应该为0!");
}
redPacketNumber--;
System.out.println(Thread.currentThread().getName() + " 抢到红包" + currentRedPacket + "分,奖励一下!");
grad(currentRedPacket);
}
// 记录已抢红包情况
private void grad(int gradRedPacket){
gradRedPacketNumber++;
this.gradRedPacket += gradRedPacket;
System.out.println("已抢红包"+gradRedPacketNumber + "个,已抢红包" + gradRedPacket + "分,剩余红包" + (redPacket - this.gradRedPacket) + "分。");
}
// 计算应抢红包额度
private int gradCurRedPacket() {
int residue = this.redPacket - this.gradRedPacket;
if (redPacketNumber == 1) return residue;
int currentRedPacket = 0;
while (currentRedPacket == 0){
currentRedPacket = random.nextInt(residue - (redPacketNumber - 1) * minRedPacket);
}
return currentRedPacket;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入红包总金额(分):");
int redPacket = scanner.nextInt();
System.out.println("请输入红包总数:");
int redPacketNumber = scanner.nextInt();
System.out.println("请输入抢红包人数");
int people = scanner.nextInt();
CountDownLatch countDownLatch = new CountDownLatch(1);
RandomGradThread grad = new RandomGradThread(redPacket,redPacketNumber,countDownLatch);
for (int i = 0; i < people; i++) {
new Thread(grad).start; // 注意此位置问题
}
countDownLatch.countDown();
}
即抽出统一的抢红包逻辑,根据不同逻辑定义不同实现:
public class RedPacketThread implements Runnable{
private final CountDownLatch countDownLatch;
private final AbstractGradRedPacket redPacket;
public RedPacketThread(CountDownLatch countDownLatch, AbstractGradRedPacket redPacket) {
this.countDownLatch = countDownLatch;
this.redPacket = redPacket;
}
@Override
public void run() {
try {
countDownLatch.await(); // 争取同时开始竞价
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
redPacket.grad();
}
}
/**
* 抢红包抽象类
*/
public abstract class AbstractGradRedPacket {
protected int redPacket;
protected int gradRedPacketNumber;
protected int gradRedPacket;
public AbstractGradRedPacket(int redPacket) {
this.redPacket = redPacket;
}
abstract void grad();
// 收集已抢红包情况
public synchronized void grad(int gradRedPacket){
gradRedPacketNumber++;
this.gradRedPacket += gradRedPacket;
System.out.println("已抢红包"+gradRedPacketNumber + "个,已抢红包" + gradRedPacket + "分,剩余红包" + (redPacket - this.gradRedPacket) + "分。");
}
}
/**
* 固定红包总额和数量的抢红包抽象逻辑
*/
public abstract class FixRedPacket extends AbstractGradRedPacket{
protected int redPacketNumber;
public FixRedPacket(int redPacket,int redPacketNumber) {
super(redPacket);
this.redPacketNumber = redPacketNumber;
}
@Override
synchronized void grad() {
if (redPacketNumber == 0){
System.out.println(Thread.currentThread().getName() + " 未抢到,感觉错过了一个亿!");
return;
}
int currentRedPacket = gradCurRedPacket();
// assert currentRedPacket > 0:"红包大小不应该为0!";
if (currentRedPacket == 0){
throw new RuntimeException("红包大小不应该为0!");
}
redPacketNumber--;
System.out.println(Thread.currentThread().getName() + " 抢到红包" + currentRedPacket + "分,奖励一下!");
grad(currentRedPacket);
}
abstract int gradCurRedPacket();
}
/**
* 固定红包
*/
public class FailGradRedPacket extends FixRedPacket{
private final int redPacketSize;
private final int lastRedPacketSize;
public FailGradRedPacket(int redPacket, int redPacketNumber) {
super(redPacket,redPacketNumber);
this.redPacketSize = redPacket / redPacketNumber;
this.lastRedPacketSize = this.redPacketSize + redPacket % redPacketNumber;
System.out.println("。。。公平红包逻辑。。。");
}
@Override
int gradCurRedPacket() {
int currentRedPacket = redPacketSize;
if (redPacketNumber == 1){
currentRedPacket = lastRedPacketSize;
}
return currentRedPacket;
}
}
/**
* 随机红包
*/
public class RandomGradRedPacket extends FixRedPacket{
private final Random random = new Random();
private final int minRedPacket = 1;
public RandomGradRedPacket(int redPacket,int redPacketNumber) {
super(redPacket,redPacketNumber);
System.out.println("。。。随机红包逻辑。。。");
}
@Override
synchronized int gradCurRedPacket() {
int residue = this.redPacket - this.gradRedPacket;
if (redPacketNumber == 1) return residue;
int currentRedPacket = 0;
while (currentRedPacket == 0){
currentRedPacket = random.nextInt(residue - (redPacketNumber - 1) * minRedPacket);
}
return currentRedPacket;
}
private int gradRedPacket(int residue){
int currentRedPacket = 0;
while (currentRedPacket == 0){
currentRedPacket = random.nextInt(residue - (redPacketNumber - 1) * minRedPacket);
}
return currentRedPacket;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入红包总金额(分):");
int redPacket = scanner.nextInt();
System.out.println("请输入红包总数:");
int redPacketNumber = scanner.nextInt();
System.out.println("请输入抢红包人数");
int people = scanner.nextInt();
CountDownLatch countDownLatch = new CountDownLatch(1);
AbstractGradRedPacket gradRedPacket;
// gradRedPacket = new FailGradRedPacket(redPacket,redPacketNumber);
gradRedPacket = new RandomGradRedPacket(redPacket,redPacketNumber);
RedPacketThread redPacketThread = new RedPacketThread(countDownLatch,gradRedPacket);
for (int i = 0; i < people; i++) {
new Thread(redPacketThread).start();
}
countDownLatch.countDown();
}
请输入红包总金额(分):
100
请输入红包总数:
10
请输入抢红包人数
12
。。。随机红包逻辑。。。
Thread-3 抢到红包38分,奖励一下!
已抢红包1个,已抢红包38分,剩余红包62分。
Thread-7 抢到红包42分,奖励一下!
已抢红包2个,已抢红包42分,剩余红包20分。
Thread-4 抢到红包7分,奖励一下!
已抢红包3个,已抢红包7分,剩余红包13分。
Thread-5 抢到红包1分,奖励一下!
已抢红包4个,已抢红包1分,剩余红包12分。
Thread-8 抢到红包1分,奖励一下!
已抢红包5个,已抢红包1分,剩余红包11分。
Thread-6 抢到红包6分,奖励一下!
已抢红包6个,已抢红包6分,剩余红包5分。
Thread-2 抢到红包1分,奖励一下!
已抢红包7个,已抢红包1分,剩余红包4分。
Thread-9 抢到红包1分,奖励一下!
已抢红包8个,已抢红包1分,剩余红包3分。
Thread-1 抢到红包1分,奖励一下!
已抢红包9个,已抢红包1分,剩余红包2分。
Thread-10 抢到红包2分,奖励一下!
已抢红包10个,已抢红包2分,剩余红包0分。
Thread-11 未抢到,感觉错过了一个亿!
Thread-0 未抢到,感觉错过了一个亿!