问题背景
目前业务有一个需求, 客户想要导出近3年的各模块数据汇总成xlxs表格,数据库数据达1亿+;
以及面试过程经常问到的,了解Java并发包吗,有CountDownLatch闭锁真实业务使用过吗,如何吹牛b
问题难点
很多同学会想到,异步导出,即前端服务请求后台,后台检验基本参数后,
直接返回前端,提示‘正在后台导出任务,请稍后至文件下载中心查看文件’,然后开启后台异步线程去做 数据收集,汇总,生成xlxs

但是这里依旧有一个问题,刚才提到过,后台数据库数据达1亿,并且这张xlxs的表格数据,可能来源于查询A模
块,B模块,C模块等.....,假设A模块处理耗时30s,B模块耗时60s,C模块耗时90s,那么会导致后台线程的运行时间很长很长,
也就是后台看似是异步线程,实际逻辑走的是同步查询,从而导致整个后台线程耗时很长,
从而 *********** 错失年终,拿到大礼包,如图楼主个人未优化之前的后台耗时达15s

解决思路CountDownLatch闭锁
我这里的主要想法是 后台既然真实逻辑是按同步走的,那我能不能将后台的 ‘同步’ 搞成 异步来处理,如图

就好像:打LOL的时候,点击开始游戏(后台开启),之后进入等待页面,正在等待玩家一,等待玩家二...等待玩家三(异步处理),最后所有玩家都准备好后,然后游戏正式开始(主线程运行)
这里写一段伪代码,可以copy下来去感受下
public class CountDownLatchCompare {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < countDownLatch.getCount(); i++) {
new Thread(new MyThread(countDownLatch), "玩家" + i).start();
}
System.out.println("正在等待所有玩家准备好");
countDownLatch.await();
System.out.println("开始游戏 go go go");
}
private static class MyThread implements Runnable {
private CountDownLatch countDownLatch;
private MyThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Random rand = new Random();
int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;
Thread.sleep(randomNum);
System.out.println(Thread.currentThread().getName() + " 已经准备好了, 所使用的时间为 " + ((double) randomNum / 1000) + "s");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

因此对于刚才提到的问题,这里完全可以用同样的思想用CountDownLatch闭锁去处理,即
后台线程访问A,B,C不同服务采集表格数据时,采用CountDownLatch和ExecutorService进行异步处理,
即用ExecutorService开启对应的A,B,C等数据的后台采集
同时用CountDownLatch闭锁去监控A,B,C服务是否完成,当A,B,C都完成时,再进行数据装填进xlxs
ExecutorService executorService = Executors.newFixedThreadPool(3);
final CountDownLatch countDownLatch = new CountDownLatch(3);
executorService.submit(new Runnable() {
@Override
public void run () {
countDownLatch.countDown();
}
});
executorService.submit(new Runnable() {
@Override
public void run () {
countDownLatch.countDown();
}
});
executorService.submit(new Runnable() {
@Override
public void run () {
countDownLatch.countDown();
}
});
countDownLatch.await();
executorService.shutdown();
效果展示:优化了三分之一时间

备注
后续再梳理一下concurrent并发包下的CyclicBarrier具体业务场景