本文是“Java不生成临时文件的导出下载csv文件的功能实现(1)一文的扩展,所以模拟输入和下载文件的代码同上篇文章。

在本系列第二篇文章中,我们每次都会把一个csv文件所需要的数据全部查询出来,方便是方便了,可是数据库的压力就上去了,因为最终生成的一万条数据,可能需要从数据库中查询几十万条数据作为基础,所以,我们还需要对代码做进一步的优化。

在这里,我们使用线程池和future来进行处理。

线程池代码如下:

@Configuration
@EnableAsync
public class TaskExecutePool {
    @Bean("exportExecutor")
    public ThreadPoolExecutor getExportExecutor() {
        // 创建队列
        BlockingQueue blockingQueue = new LinkedBlockingDeque<>(120);
        // 核心5 最大10 队列120 超时60s 拒绝策略报异常
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,blockingQueue);
        return threadPoolExecutor;
    }
}

然后,当然是要定义每次查询的数量。

private static final Integer groupCapacity = 2000;

接着,按照2000进行分组,得出组数。

        Integer exportTotal = dataList.size();
        Integer groupNum = (exportTotal + groupCapacity - 1) / groupCapacity;//计算组数
        log.info("总数" + exportTotal + ", 每组" + groupCapacity + ", 组数" + groupNum);

使用Future和线程池请求调用获取导出列表。

        List futures = new ArrayList<>();
        for (Integer i = 0; i < groupNum; i++) {
            try {
                // 多线程调用获取list
                Future future = exportExecutor.submit(new CallableTask(dataList, i));
                futures.add(future);
            } catch (RejectedExecutionException e) {
                // 系统繁忙说明队列已满
                log.error("第" + i + "次请求队列已满", e);
            } catch (Exception e) {
                log.error("获取导出列表异常" + e);
            }
        }

每满5组,进行一次csv文件组装。

            //待生成文件的数据列表
            List exportList = new ArrayList();
            //返回数据的组数,每满5组,进行一次csv文件组装
            int exportCount = 0;
            //生成文件编号
            int exportIndex = 0;
            while (true) {
                //有未完成的future
                if (futures != null && !futures.isEmpty()) {
                    for (int i = 0; i < futures.size(); i++) {
                        Future future = futures.get(i);
                        // 判断future是否执行完成
                        if (future.isDone()) {
                            //从future中获取单次2000条数据,存入exportList中
                            exportList.addAll((Collection) future.get());
                            futures.remove(i);
                            i--;
                            exportCount++;
                            //累计获取五次时,重置exportCount,生成对应csv文件,然后清空exportList
                            if (exportCount >= 5) {
                                exportCount = 0;
                                exportIndex++;
                                // 导出excel
                                byte[] bytes = this.generateCsvFile(exportList, fieldNames, fieldDescs);
                                bytesList.add(bytes);
                                exportList.clear();
                            }
                        }
                    }
                } else {//future全部完成
                    //如果exportCount不为零,说明还有不足1w条的数据未生成csv文件
                    if (exportCount > 0) {
                        exportIndex++;
                        // 导出excel
                        byte[] bytes = this.generateCsvFile(exportList, fieldNames, fieldDescs);
                        bytesList.add(bytes);
                        exportList.clear();
                    }
                    break;
                }
            }

线程池中执行的方法如下。

    private class CallableTask implements Callable {
        private List dataList;
        private Integer i;

        public CallableTask(List dataList, Integer i) {
            this.i = i;
            this.dataList = dataList;

        }

        @Override
        public List call() throws Exception {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("调用查询接口");
            //睡眠一会
            sleep(new Random().nextInt(2000));
            List subList = null;
            //最后一组的i和组容量的乘积大于列表总大小,会造成下面else中的subList越界
            if (i * groupCapacity < dataList.size()) {
                //最后不足2000的数据
                if ((i + 1) * groupCapacity > dataList.size()) {
                    subList = dataList.subList(i * groupCapacity, dataList.size());
                } else {
                    subList = dataList.subList(i * groupCapacity, (i + 1) * groupCapacity);
                }
            }
            stopWatch.stop();
            log.info(stopWatch.prettyPrint());

            return subList;
        }
    }

具体有不清楚的,可以去我的github看看。地址:

https://github.com/whol/exportdemo,分支为 使用线程池导出多cvs打包zip。

 

 

最后修改于 2019-04-02 20:52:07
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇