Spring Boot分段处理List集合多线程批量插入数据

项目场景:

大数据量的List集合,需要把List集合中的数据批量插入数据库中。


解决方案:

拆分list集合后,然后使用多线程批量插入数据库

1.实体类

package com.test.entity;import lombok.Data;@Data
public class TestEntity {private String id;private String name;
}

2.Mapper

如果数据量不大,用foreach标签就足够了。如果数据量很大,建议使用batch模式。

package com.test.mapper;import java.util.List;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;import com.test.entity.TestEntity;public interface TestMapper {/*** 1.用于使用batch模式,ExecutorType.BATCH开启批处理模式* 数据量很大,推荐这种方式*/@Insert("insert into test(id, name) "+ " values"+ " (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR})")void testInsert(TestEntity testEntity);/*** 2.使用foreach标签,批量保存* 数据量少可以使用这种方式*/@Insert("insert into test(id, name) "+ " values"+ " <foreach collection='list' item='item' index='index' separator=','>"+ " (#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR})"+ " </foreach>")void testBatchInsert(@Param("list") List<TestEntity> list);
}

3.spring容器注入线程池bean对象

package com.test.config;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
@EnableAsync
public class ExecutorConfig {/*** 异步任务自定义线程池*/@Bean(name = "asyncServiceExecutor")public Executor asyncServiceExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(50);//配置最大线程数executor.setMaxPoolSize(500);//配置队列大小executor.setQueueCapacity(300);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix("testExecutor-");// rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//调用shutdown()方法时等待所有的任务完成后再关闭executor.setWaitForTasksToCompleteOnShutdown(true);//等待所有任务完成后的最大等待时间executor.setAwaitTerminationSeconds(60);return executor;}
}

4.创建异步线程业务类

package com.test.service;import java.util.List;
import java.util.concurrent.CountDownLatch;import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;import com.test.entity.TestEntity;
import com.test.mapper.TestMapper;@Service
public class AsyncService {@Autowiredprivate SqlSessionFactory sqlSessionFactory;@Async("asyncServiceExecutor")public void executeAsync(List<String> logOutputResults, CountDownLatch countDownLatch) {try{//获取session,打开批处理,因为是多线程,所以每个线程都要开启一个事务SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);TestMapper mapper = session.getMapper(TestMapper.class);//异步线程要做的事情for (int i = 0; i < logOutputResults.size(); i++) {System.out.println(Thread.currentThread().getName() + "线程:" + logOutputResults.get(i));TestEntity test = new TestEntity();//test.set()//.............//批量保存mapper.testInsert(test);//每1000条提交一次防止内存溢出if(i%1000==0){session.flushStatements();}}//提交剩下未处理的事务session.flushStatements();}finally {countDownLatch.countDown();// 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放}}
}

5.拆分list调用异步的业务方法

package com.test.service;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;import javax.annotation.Resource;import org.springframework.stereotype.Service;@Service
public class TestService {@Resourceprivate AsyncService asyncService;public int testMultiThread() {List<String> logOutputResults = getTestData();//按线程数拆分后的listList<List<String>> lists = splitList(logOutputResults);CountDownLatch countDownLatch = new CountDownLatch(lists.size());for (List<String> listSub:lists) {asyncService.executeAsync(listSub, countDownLatch);}try {countDownLatch.await(); //保证之前的所有的线程都执行完成,才会走下面的;// 这样就可以在下面拿到所有线程执行完的集合结果} catch (Exception e) {e.printStackTrace();}return logOutputResults.size();}public List<String> getTestData() {List<String> logOutputResults = new ArrayList<String>();for (int i = 0; i < 3000; i++) {logOutputResults.add("测试数据"+i);}return logOutputResults;}public List<List<String>> splitList(List<String> logOutputResults) {List<List<String>> results = new ArrayList<List<String>>();/*动态线程数方式*/// 每500条数据开启一条线程int threadSize = 500;// 总数据条数int dataSize = logOutputResults.size();// 线程数,动态生成int threadNum = dataSize / threadSize + 1;/*固定线程数方式// 线程数int threadNum = 6;// 总数据条数int dataSize = logOutputResults.size();// 每一条线程处理多少条数据int threadSize = dataSize / (threadNum - 1);*/// 定义标记,过滤threadNum为整数boolean special = dataSize % threadSize == 0;List<String> cutList = null;// 确定每条线程的数据for (int i = 0; i < threadNum; i++) {if (i == threadNum - 1) {if (special) {break;}cutList = logOutputResults.subList(threadSize * i, dataSize);} else {cutList = logOutputResults.subList(threadSize * i, threadSize * (i + 1));}results.add(cutList);}return results;}
}

5.Controller测试

@RestController
public class TestController {@Resourceprivate TestService testService;@RequestMapping(value = "/log", method = RequestMethod.GET)@ApiOperation(value = "测试")public String test() {testService.testMultiThread();return "success";}
}

总结:

注意这里执行插入的数据是无序的。

Java多线程分段处理List集合_java 每个list分配一个线程-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/827391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python入门:使用Python实现简单的猜数字游戏

Python是一种解释型、交互式、面向对象的编程语言&#xff0c;其设计哲学强调代码的可读性&#xff0c;并允许开发者用少量代码表达想法。在本文中&#xff0c;我们将通过编写一个简单的猜数字游戏来展示Python的基础知识和编程逻辑。 游戏描述 游戏开始时&#xff0c;程序会…

数字化转型过程中所面临的挑战以及应对这些挑战的策略

随着信息技术的快速发展&#xff0c;企业为了提升运营效率、优化资源配置&#xff0c;往往会在不同部门建立各自的信息系统。然而&#xff0c;这些系统的孤立性、功能重叠以及数据不一致性等问题逐渐凸显&#xff0c;导致企业业务流程受阻&#xff0c;难以形成高效的协同作战能…

Spring源码中的简单工厂模式

Spring 源码中广泛运用了各种设计模式,其中包括简单工厂模式。简单工厂模式在 Spring 中主要用于简化对象的创建过程,将对象的创建逻辑集中管理,从而使得客户端代码无需关心具体的对象创建细节,只需与工厂交互就能获取所需的对象实例。这种设计有助于提高代码的可读性、可维…

高斯溅射融合之路(一)- webgl渲染3d gaussian splatting

大家好&#xff0c;我是山海鲸的技术负责人。之前已经写了一个GIS融合系列。其实CesiumJS的整合有相当的难度&#xff0c;同时也有很多方面的工作&#xff0c;很难在几篇文章内写完&#xff0c;整个山海鲸团队也是投入了接近两年的时间&#xff0c;才把周边整套工具链进行了完善…

prometheus服务端部署

prometheus 安装 下载 # https://prometheus.io/download/wget https://github.com/prometheus/prometheus/releases/download/v2.34.0/prometheus-2.34.0.linux-amd64.tar.gztar -zxf prometheus-2.34.0.linux-amd64.tar.gz -C /data0/prometheus-2.34.0sudo adduser prome…

Linux中inode号与日志分析

一.inode号 1.inode表结构 元信息&#xff1a;每个文件的属性信息&#xff0c;比如&#xff1a;文件的大小&#xff0c;时间&#xff0c;类型&#xff0c;权限等&#xff0c;称为文件的元数据(meta data 元信息 ) 元数据是存放在inode&#xff08;index node&#xff09;表中…

Spring Kafka—— KafkaListenerEndpointRegistry 隐式注册分析

由于我想在项目中实现基于 Spring kafka 动态连接 Kafka 服务&#xff0c;指定监听 Topic 并控制消费程序的启动和停止这样一个功能&#xff0c;所以就大概的了解了一下 Spring Kafka 的几个重要的类的概念&#xff0c;内容如下&#xff1a; ConsumerFactory 作用&#xff1a;…

Opencv_2_ 图像色彩空间转换

ColorInvert.h 内容如下&#xff1a; #pragma once #include <opencv.hpp> using namespace std; #include <opencv.hpp> using namespace cv; using namespace std; class ColorInvert{ public : void colorSpaceInvert(Mat&image); }; ColorInvert.cpp…

使用了Python语言和Flask框架。创建一个区块链网络,允许用户通过HTTP请求进行交互,如包括创建区块链、挖矿、验证区块链等功能。

目录 大概来说&#xff1a; 二、代码注释 1.添加交易方法&#xff08;add_transaction函数&#xff09; 2.添加新的节点&#xff08;add_node 函数&#xff09; 3、替换链的方法&#xff08;replace_chain函数&#xff09; 总结 大概来说&#xff1a; 定义了一个名为Blo…

1、初识Linux系统 shell 脚本

shell脚本组成介绍 1、shell脚本命名规则 必须以.sh文件结尾&#xff0c;类似.txt文件&#xff1b;例如创建一个test.sh文件 2、linux系统中shell脚本开头&#xff0c;必须以如下代码开头 test.sh内容如下&#xff1a; #!/bin/bash# 这是一个注释echo "Hello, World!&…

西安地区调频广播频率一览

FM89.6 陕西人民广播电台经济广播 FM91.6 陕西人民广播电台交通广播 FM93.1 西安人民广播电台音乐广播 FM95.5 中央人民广播电台音乐之声 FM96.4 中央人民广播电台中国之声 FM98.8 陕西人民广播电台音乐广播 FM101.1 陕西人民广播电台戏曲广播 FM101.8 陕西人民广播电台…

高效过滤器检漏方法选择指南及关键注意事项一览

在生物制药企业中&#xff0c;高效过滤器&#xff08;HEPA&#xff09;的检漏工作是确保洁净室能够达到并保持设计的洁净级别的关键步骤。这关系到产品的质量和安全&#xff0c;因此必须遵循相关法规标准和操作流程。 关于北京中邦兴业 北京中邦兴业科技有限公司是一家国家高新…

element中file-upload组件的提示‘按delete键可删除’,怎么去掉?

问题描述 element中file-upload组件会出现这种提示‘按delete键可删除’ 解决方案&#xff1a; 这是因为使用file-upload组件时自带的提示会盖住上传的文件名&#xff0c;修改一下自带的样式即可 ::v-deep .el-upload-list__item.is-success.focusing .el-icon-close-tip {d…

2.微服务技术

微服务技术对比 DubboSpringCloudSpringCloudAlibaba注册中心zookeeper&#xff0c;RedisEureka,ConsulNacos,Eureka服务远程调用Dubbo协议Feign(http协议)Dubbo,Feign配置中心SpringCloudConfigSpringCloudConfig,Nacos服务网关SpringCloudGateway,ZuulSpringCloudGateway,Zu…

css 设置div阴影样式

文章目录 问题描述解决方案代码示例设置阴影样式 问题描述 大家好&#xff01;我是夏小花&#xff0c;今天是2024年4月23日|农历三月十五&#xff0c;今天这篇文章主要以div或者是view添加阴影样式为主题 &#xff0c;详细代码如下 解决方案 这段是vue页面中的 代码&#xff0…

基于SpringBoot的宠物领养网站管理系统

基于SpringBootVue的宠物领养网站管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 宠物领养 宠物救助站 宠物论坛 登录界面 管理员界面 摘要 基于Spr…

1.C++入门

目录 1.C关键字 2.命名空间 作用域方面的优化 a.命名空间定义 b.命名空间使用 3.C 输入&输出 1.C关键字 C有63个关键字&#xff0c;C语言有32个关键字&#xff0c;存在重叠如荧光笔标出 2.命名空间 作用域方面的优化 如果变量&#xff0c;函数和类的名称都存在于全…

vue-router学习4:嵌套路由

一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下&#xff0c;URL 的片段通常对应于特定的嵌套组件结构。 定义子路由组件 首先&#xff0c;你需要为嵌套路由创建对应的子组件。这些组件将在父路由组件的 <router-view></router-view> 中被渲染。 <!-…

AI自动生成PPT文档 aippt的API介绍文档

官方链接直达&#xff01; 产品介绍​ 能力介绍​ AiPPT 是一款智能生成演示幻灯片的在线工具。专业设计团队打造海量模板资源&#xff0c;输入标题即可轻松生成完整的PPT。同时 AiPPT 支持导入多格式文档一键生成 PPT&#xff0c;让 PPT 创作更加高效。聚焦于内容&#xff0…

安装zabbix server

目录 1、实验环境 2、yum 安装zabbix server 2.1 解决权限问题和放行流量 2.2 安装zabbix-server 1、实验环境 操作系统rhel8zabbix6.0TLS数据库mysql8.0.30IP地址192.168.81.131时间配置NTP时间服务器同步 2、yum 安装zabbix server 如果通过yum源安装&#xff0c;操作系…