没有写过抢红包的处理程序,考虑到多线程是核心,所以练习先写一个本地使用多线程来抢红包的模拟程序。程序运行要达到的效果,是最终红包都被抢完,并对数据进行统计,统计结果和总库存要完全吻合。
没有过多的解释,直接就一个测试类。
package com.chris.java;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Chris Chan
* 2020/2/11 19:27
* Use for:
* Explain: 线程测试
*/
public class TestThread {
private static volatile int amount = 1143;//红包总数
private static Map<String, Integer> map = new HashMap<>(16);//记录每个线程抢到的红包
private static Object boxLock = new Object();//线程锁 可以假设是装红包的盒子
public static void main(String[] args) {
action();
}
//抢红包
private static void action() {
Runnable runnable = () -> {
String threadName = Thread.currentThread().getName();
while (amount > 0) {
//抢一次 独占这把锁 加锁部分的操作应该越少越好,减少时间消耗,可以提升性能
synchronized (boxLock) {
/*
可以想象,在最后的一个红包被抢前,有一大堆人(线程)在后面排队获取这把锁,
如果让这些已经进入循环的线程继续,则后面这些线程可能会继续抢一些
不存在的红包,使红包库存编程负数。所以需要在这里检查一下盒子里面
是否空空如也。
*/
if (amount <= 0) {
break;
}
amount--;//减库存 如果不想打印控制台信息,就用这一行替换下面一行
//减库存放在这一行中,是为了避免两次控制台输出造成过多的时间消耗
//System.out.println("红包库存 " + (amount--) + "个 线程 " + threadName + " 抢到一个红包 还剩 " + amount + " 个");
//下面这一行代码只是为了便于检查,其实不必要,会增加时间消耗
// if (amount % 5 == 0) {
// System.out.println();
// }
}
Integer total = map.get(threadName);
if (null == total) {
map.put(threadName, 1);
} else {
map.put(threadName, total + 1);
}
}
};
Thread thread01 = new Thread(runnable);
Thread thread02 = new Thread(runnable);
Thread thread03 = new Thread(runnable);
thread01.start();
thread02.start();
thread03.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n汇总:\n红包还剩 " + amount + " 个");
map.keySet().stream().forEach(key -> System.out.println("\t" + key + " : " + map.get(key)));
System.out.println("红包一共被领取 " + map.values().stream().reduce(0, (v1, v2) -> v1 + v2) + " 个");
}
}
开始时遇到一些问题,就是变-1,后来添加来加锁后检查的逻辑才解决。一般这种问题都是解决问题的思路需要调整,生活中如何解决问题,程序中就如何实现逻辑。解决了前者,后者也就不是什么问题。另外加锁后的操作应该尽可能少消耗时间,否则可能造成时间片消费的不均衡。
运行结果:
换过几次参数都没有什么问题,不过这只是本地测试,逻辑要简单很多。下次写一个全套的前后端测试程序,需要用好http请求,红包数据要放在数据库。