Java线程操作-模拟本地多线程抢红包

没有写过抢红包的处理程序,考虑到多线程是核心,所以练习先写一个本地使用多线程来抢红包的模拟程序。程序运行要达到的效果,是最终红包都被抢完,并对数据进行统计,统计结果和总库存要完全吻合。

没有过多的解释,直接就一个测试类。

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 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,后来添加来加锁后检查的逻辑才解决。一般这种问题都是解决问题的思路需要调整,生活中如何解决问题,程序中就如何实现逻辑。解决了前者,后者也就不是什么问题。另外加锁后的操作应该尽可能少消耗时间,否则可能造成时间片消费的不均衡。

运行结果:

Java线程操作-模拟本地多线程抢红包_第1张图片

换过几次参数都没有什么问题,不过这只是本地测试,逻辑要简单很多。下次写一个全套的前后端测试程序,需要用好http请求,红包数据要放在数据库。

你可能感兴趣的:(Java线程操作-模拟本地多线程抢红包)