并发编程学习案例-单线程和多线程1加到1000W耗时对比

前言

从1开始每次加1 加1000万次,你会怎么使用多线程的方式来加快计算速度,并且保证结果一致?这个问题我在以前校招的时候被问到过,当时一下懵逼。最近刚好体系学习了下多线程,趁热实现了一下。

代码案例

在这个场景里,如果多个线程对一个数据进行加1操作的话,那个数据就是一个热点数据,是要进行并发安全保护,也就是加锁,这样可能实际上并不会比单线程加来的快,因为创建线程,线程间等待锁释放的cpu调度也是需要耗费时间的。麻雀虽小考察点却不少,考察点如下:

  1. 需要把热点数据进行拆分,最后进行汇总,提高并发度。
  2. 如何进行数据量拆分,保证结果一致
  3. 你对并发这块的JUC的使用和熟悉情况

核心思路就是 把任务均分给多个线程,每个线程执行各自的加1操作,然后等每个线程执行完毕后,在主线程里挨个进行累加。

/**
 * @author: lvzb31988
 * @date: 2023/02/02 9:41
 */
@Slf4j
public class PlusTest {

    public static class PlusTask implements Callable<Long> {
        private Long range;
        private Long num;

        public PlusTask(Long range, Long num) {
            this.range = range;
            this.num = num;
        }

        @Override
        public Long call() throws Exception {
            for (int i = 0; i < range; i++) {
            // 休眠一毫秒是为了模拟 加 1的这次操作耗时,不加的话执行太快了,无法看到明显的对比差异
                Thread.sleep(1);
                num++;
            }
            return num;
        }
    }


    @Test
    void onePlus() throws InterruptedException {
        long start = System.currentTimeMillis();
        // 1000W 太多了,这里给个1000,知道如何实现和看到明显对比即可
        final int dataRange = 1000;
        long total = 0;
        for (int i = 0; i < dataRange; i++) {
        // 休眠一毫秒是为了模拟 加 1的这次操作耗时,不加的话执行太快了,无法看到明显的对比差异
            Thread.sleep(1);
            total++;
        }
        // 耗时:1460 ms,total:1000
        log.warn("耗时:{} ms,total:{}", System.currentTimeMillis() - start, total);
        System.out.println("耗时:total: " + total);
    }

    /**
     * 问题一: 均分
     * 问题二: 为何线程池submit返回的 future get会报空指针
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    @Test
    void asyncOnePlus() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        // 数据总量
        final long dataRange = 1000;
        // 线程数
        final int threadNum = 5;
        // 均分数据总量
        long avgRange = dataRange / threadNum;
        List<Future<Long>> futureList = new ArrayList<>();
        ExecutorService pool = Executors.newFixedThreadPool(threadNum);
        for (int i = 1; i <= threadNum; i++) {
            Future<Long> future = null;
            if (i == threadNum) {
                // 注意:需要特殊处理最后一个线程的处理量,不然分不完
                future = pool.submit(new PlusTask(dataRange - (avgRange * (threadNum - 1)), 0L));
            } else {
                future = pool.submit(new PlusTask(avgRange, 0L));
            }
            futureList.add(future);
        }

        Long total = 0L;
        for (int i = 0; i < futureList.size(); i++) {
            Long num = futureList.get(i).get();
            total += num;
        }
        // 耗时:284 ms,total:1000
        log.warn("耗时:{} ms,total:{}", System.currentTimeMillis() - start, total);
    }
}

结论

  • 使用5个线程,单线程 耗时:1460 ms,5个线程 耗时:284 ms。可以多执行几次看看,设计合理基本上能快接近4-5倍的理论值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值