Java 多线程在项目中的实战应用

引言

当我们在项目遇到一次 IO 密集型的操作,会进行大量的数据库查询,我们可以考虑使用多线程来解决这个问题。

背景

首先看下这个需求的具体流程,数据库中保存了所有的订单数据,现在老板让我统计某个时间段里每天的订单数量以及订单状态,由于早期设计问题,订单表中使用的是时间戳对时间进行存储,这导致了我们没有办法使用类似 group by 的方式进行直接分类,再加上也不想写一些复杂 sql,所以准备以传入的起始时间为起点,遍历从这个时间点起每天的数据。

问题陈述

相信通过上面的描述,大家已经大概知道问题所在,那就是太耗时,如果时间段选的足够长的话,我们的遍历时间会非常的缓慢,其中最主要的操作当然就是 sql 查询,因为需要查询这个时间段里每天是否存在数据,并且进行汇总。

解决方案

因此,考虑到其中大量的都是 IO 操作,我们完全可以使用多线程的方式,将这个时间段分为有限多个时间段,然后每个线程分别对某个时间段进行数据查询,并且最终添加到我们的 list 当中。

结果

首先给大家看一下结果,效果还是非常明显。
优化前:
优化之前
优化后:
优化之后

代码

首先我们创建了一个任务类,这个任务类就是我们每个线程需要执行的任务。

class FitDailyDataTask implements Runnable {
		// 我们查询出来的任务将会放入这个列表当中
        private List<DailyDataResp> dailyDataRespList;
        // 开始的时间
        private long durationStart;
        // 结束的时间
        private long durationEnd;

		// 我们在构建这个任务的时候,传入需要的参数
        public FitDailyDataTask(List<DailyDataResp> dailyDataRespList, long durationStart, long durationEnd) {
            this.dailyDataRespList = dailyDataRespList;
            this.durationStart = durationStart;
            this.durationEnd = durationEnd;
        }

        @Override
        public void run() {
            // 循环装填整个测试数据
            long curDurationStart = durationStart;
            // 结束时间
            long curDurationEnd = 0;
            while (curDurationStart <= durationEnd) {
                curDurationEnd = curDurationStart + ONE_DAY;
                // 以下就是我们进行的 SQL 操作,这里省略了部分代码,大家自己脑补
                // 查询当前日期检查人数
                // TODO SQL操作
                // 数据查询出来之后插入我们的 list 中
                DailyDataResp dailyDataResp = new DailyDataResp();
				dailyDataRespList.add(dailyDataResp);
                curDurationStart += ONE_DAY;
            }
        }
}

这里大家需要注意一点,我们的代码中用到的列表是 Vector ,这是为了保证在多线程插入的情况下线程安全问题。
接下来就是我们的核心代码了。

/**
     * 装填列表数据
     * @param dailyDataRespList 装填的列表
     * @param durationStart 开始时间
     * @param durationEnd 结束时间
     */
    private void fitDailyDataRespList(List<DailyDataResp> dailyDataRespList, 
    	long durationStart, long durationEnd) {
    	// 创建一个线程池,核心线程数根据我们的机器来定
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                CORE_SIZE,
                CORE_SIZE,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(CORE_SIZE));
        // 这里计算出我们每个线程需要处理的时间段
        long step = (durationEnd - durationStart) / ONE_DAY / CORE_SIZE * ONE_DAY;
        // 创建任务并且提交给我们的线程池执行
        for (int i = 0; i <= CORE_SIZE; i++) {
            long start = durationStart + (step * i);
            long end = start + step - 1;
            pool.submit(new FitDailyDataTask(dailyDataRespList, start, end, hospitalId));
        }
        // 线程池使用完毕之后需要关闭,避免内存泄露问题
        pool.shutdown();
        try {
        	// 这里我们需要等待线程池执行完以上的任务,因为后续我们可能对这个列表进行其他处理
            pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

结论

以上就是我这次在项目实战当中使用多线程的一次经历,虽然平时在项目中,大家总是说可能工作个三五年都用不到一次多线程,但是面试环节多线程永远是绕不开的话题,想必也是因为这个原因,所以大家一定要好好的掌握相关知识。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值