Redian新闻
>
Java批量更新太慢?多线程+List分段完美解决!

Java批量更新太慢?多线程+List分段完美解决!

公众号新闻

点击上方“芋道源码”,选择“设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 
来源:blog.csdn.net/qq_43097201/
article/details/112452391/

写在前面

相信不少开发者在遇到项目对数据进行批量操作的时候,都会有不少的烦恼,尤其是针对数据量极大的情况下,效率问题就直接提上了菜板。

因此,开多线程来执行批量任务是十分重要的一种批量操作思路,其实这种思路实现起来也十分简单,就拿批量更新的操作举例:

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

整体流程图

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

步骤

  • 获取需要进行批量更新的大集合A,对大集合进行拆分操作,分成N个小集合A-1 ~ A-N
  • 开启线程池,针对集合的大小进行调参,对小集合进行批量更新操作。
  • 对流程进行控制,控制线程执行顺序。

按照指定大小拆分集合的工具类

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;

import java.util.List;

/**
 * 拆分结合工具类
 *
 * @author shiwen
 */

public class SplitListUtils {

    /**
     * 拆分集合
     *
     * @param <T> 泛型对象
     * @param resList 需要拆分的集合
     * @param subListLength 每个子集合的元素个数
     * @return 返回拆分后的各个集合组成的列表
     * 代码里面用到了guava和common的结合工具类
     **/

    public static <T> List<List<T>> split(List<T> resList, int subListLength) {
        if (CollectionUtils.isEmpty(resList) || subListLength <= 0) {
            return Lists.newArrayList();
        }
        List<List<T>> ret = Lists.newArrayList();
        int size = resList.size();
        if (size <= subListLength) {
            // 数据量不足 subListLength 指定的大小
            ret.add(resList);
        } else {
            int pre = size / subListLength;
            int last = size % subListLength;
            // 前面pre个集合,每个大小都是 subListLength 个元素
            for (int i = 0; i < pre; i++) {
                List<T> itemList = Lists.newArrayList();
                for (int j = 0; j < subListLength; j++) {
                    itemList.add(resList.get(i * subListLength + j));
                }
                ret.add(itemList);
            }
            // last的进行处理
            if (last > 0) {
                List<T> itemList = Lists.newArrayList();
                for (int i = 0; i < last; i++) {
                    itemList.add(resList.get(pre * subListLength + i));
                }
                ret.add(itemList);
            }
        }
        return ret;
    }

    // 运行代码
    public static void main(String[] args) {
        List<String> list = Lists.newArrayList();
        int size = 1099;
        for (int i = 0; i < size; i++) {
            list.add("hello-" + i);
        }
        // 大集合里面包含多个小集合
        List<List<String>> temps = split(list, 100);
        int j = 0;
        // 对大集合里面的每一个小集合进行操作
        for (List<String> obj : temps) {
            System.out.println(String.format("row:%s -> size:%s,data:%s", ++j, obj.size(), obj));
        }
    }

}

开启异步执行任务的线程池

public void threadMethod() {
    List<T> updateList = new ArrayList();
    // 初始化线程池, 参数一定要一定要一定要调好!!!!
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2050,
            4, TimeUnit.SECONDS, new ArrayBlockingQueue(10), new ThreadPoolExecutor.AbortPolicy());
    // 大集合拆分成N个小集合, 这里集合的size可以稍微小一些(这里我用100刚刚好), 以保证多线程异步执行, 过大容易回到单线程
    List<T> splitNList = SplitListUtils.split(totalList, 100);
    // 记录单个任务的执行次数
    CountDownLatch countDownLatch = new CountDownLatch(splitNList.size());
    // 对拆分的集合进行批量处理, 先拆分的集合, 再多线程执行
    for (List<T> singleList : splitNList) {
        // 线程池执行
        threadPool.execute(new Thread(new Runnable(){
            @Override
            public void run() {
                for (Entity yangshiwen : singleList) {
                    // 将每一个对象进行数据封装, 并添加到一个用于存储更新数据的list
                    // ......
                    
                }
            }
        }));
        // 任务个数 - 1, 直至为0时唤醒await()
        countDownLatch.countDown();
    }
    try {
        // 让当前线程处于阻塞状态,直到锁存器计数为零
        countDownLatch.await();
    } catch (InterruptedException e) {
        throw new BusinessLogException(ResponseEnum.FAIL);
    }
    // 通过mybatis的批量插入的方式来进行数据的插入, 这一步还是要做判空
    if (GeneralUtil.listNotNull(updateList)) {
        batchUpdateEntity(updateList);
        LogUtil.info("xxxxxxxxxxxxxxx");
    }
}

写在最后

多线程是Java的一个难点,但是它也很有趣,听说玩得溜得起飞的人,人生都开启多线程模式了…



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
SpringBoot 使用线程池如何控制主线程和子线程的事务又见百小僧,.NET分布式毫秒级定时任务Sundial面世专为亚洲人!墨尔本小姐姐帮您完美解决各类皮肤困扰,欢迎预约~读懂HikariCP一百行代码,多线程就是个孙子!突破困境!美国纽约首次开课《合伙人股权设计与股权设计》突破!解决!找马方!新春将至,加拿大这家西人超市十分隆重,置办年货全一站解决!快告诉身边的人!指甲变色、增厚、分层...别再去路边修脚店了!用它抹抹就能解决!美解决IRA电动车补助争议,非北美制进口车也有办法抵税ThreadLocal 搭配线程池使用造成内存泄漏的原因和解决方案[电脑] 无需烙铁,完美解决鼠标双击问题【内所】安杰,上海实习,争议解决!政府花两千万解决!BK中餐老板:我欢迎!这里一直很危险!阴暗!险象环生!学习类实用杂志合集:语数英考试难点,都用日常阅读+积累解决!福建南平游乐园管理商金生游乐纳斯达克路演PPT分享 (26页)支付宝:多线程事务怎么回滚?说用 @Transactional 可以回去等通知了!德奥匈捷四国五城游: 准备和攻略Bunnings这款11刀的神器火了,完美解决了一个困扰澳洲人的难题!《时间煮雨 》 祝网亲们感恩节快乐!记得交真人秀照片!!【小雪】重庆天寒地冻· 妈妈的小确幸【资讯】工作还没找到?!毕业换居留怎么办??!万能菌菌给大家带来完美解决方案!成功率超高!!Erayak Power Solution Group(雷亚电子)路演PPT分享文学艺术的源泉究竟是从何而来如果学校没有给面试机会是不是基本上就没戏了?SSAT分数是否比ISEE更有说服力?| FS问答精选【分数线】牛津放榜,MAT、PAT分数线随之公布,今年拿offer的人笔试都啥成绩?iPhone内存不够怎么办?5秒教你解决!爆肝50道面试题,彻底帮你搞定多线程面试!工作还没找到?!毕业换居留怎么办??!万能菌菌给大家带来完美解决方案!成功率超高!!澳洲华人及留学生,你生活中所有需求,这里都能帮你解决!【爱在深秋】抖音神曲 乌鸦说情话一碰就头疼的 Kafka 消息重复问题,立马解决!巴基斯坦这个生存问题,必须解决!推广|| 90%发型烦恼,用这招就能解决!每逢换季,皮肤起皮发痒,小心变成敏感肌!这招能解决!从JVM虚拟机到多线程,手撸Java开发面试必备技术栈 | 极客时间Spring 多线程异步上传图片、处理水印、缩略图
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。