前言:本篇文章系作者本人学习CountDownLatch处理工作问题之心得记录,仅供参考和学习
一、什么问题?
甲方有个需求,需要按照excel中某个字段count,n为值(n可能是小数,负数,正整数),生成n条数据,这个值可能为几十,也可能为几万,最后导致生成的数据会异常庞大,直接处理非常容易出现OOM问题,响应时间过长。
二、解决思路
1、对于大量的数据,不能一次性写入数据库,只能分批写入
2、考虑到n可能为1,避免不必要的计算,便需要分别处理n为正整数,n非正整数的数据
3、写入完成后,需要通知用户,便想到CountDownLatch这个方法可以等所有子线程执行完成后执行主线程。
三、实战(简单代码)
1、拆分数据
// 将excel数据根据count区分正整数和非正整数 List<ExcelBeanEntity> entities = excelListMap.get(true);2、使用CountDownLatch处理数据
// 创建两个线程写入数据 CountDownLatch latch = new CountDownLatch(2); // 线程1处理非正整数 Thread thread1 = new Thread(() -> { try { // 从map中拿到对应的数据,非正整数无需生成 List<ExcelBeanEntity> entities = listMap.get(false); this.saveBatch(entities); } catch (Exception e) { // 将当前线程的中断标志重新设置为 true。 Thread.currentThread().interrupt(); } finally { // 无论线程如何,必须减1 latch.countDown(); } }); Thread thread2 = new Thread(() -> { try { List<ExcelBeanEntity> entities = listMap.get(true); // 分批写入-每次一千条 int batchSize = 1000; List<ExcelBeanEntity> arrayList = new ArrayList<>(batchSize); for (ExcelBeanEntity entity : entities) { int n = entity.getCount().intValueExact(); for (int i= 1; i<=n;i++){ // 数据量大,避免使用copy,加大开销 ExcelBeanEntityen = new ExcelBeanEntity(); en.setName(entity.getName()); // 此时忽略后续get,set ...... arrayList.add(en); // 执行分批保存 if (arrayList.size() >= batchSize){ this.saveBatch(arrayList,arrayList.size()); arrayList.clear(); } } } // 处理不满足分批保存的剩余数据 if (!CollectionUtils.isEmpty(arrayList)){ this.saveBatch(arrayList); } } catch (Exception e) { Thread.currentThread().interrupt(); } finally { latch.countDown(); } }); // 线程执行 thread1.start(); thread2.start(); // 等待所有子线程执行完成 latch.await();3、注意点
用的是mysql数据库,需要配置批量写入开启,可以大幅度提升写入数据效率(实测20w+数据从请求到写入数据库30s左右)
rewriteBatchedStatements=true
示例:
spring: datasource: url: jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
四、总结
以前遇到大数据写入,第一想法就是异步写入,写法复杂、阅读性差。后面了解到CountDownLatch,这也算是我的初次在工作中使用,写法简单易懂。如果屏幕前的大佬还有什么好的解决方案,可以放在评论区,指导我一下,供我学习,谢谢各位OG,Bye~。