Spring Boot中线程池使用

说明:在一些场景,如导入数据,批量插入数据库,使用常规方法,需要等待较长时间,而使用线程池可以提高效率。本文介绍如何在Spring Boot中使用线程池来批量插入数据。

搭建环境

首先,创建一个Spring Boot项目,pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><groupId>com.hezy</groupId><artifactId>thread_pool_demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.6</version></dependency></dependencies>
</project>

写一个插入数据的Mapper方法

import com.hezy.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface UserMapper {@Insert("insert into i_users (username, password) values (#{user.username}, #{user.password})")void insert(@Param("user") User user);
}

写一个接口,用来插入20万条记录,如下:

import com.hezy.pojo.User;
import com.hezy.service.AsyncService;
import com.hezy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;@RestController
@RequestMapping("user")
public class UserController {/*** 总记录数*/private final static int SIZE = 40 * 10000;@Autowiredprivate UserService userService;@Autowiredprivate AsyncService asyncService;@GetMapping("insert1")public void insert1() {ArrayList<User> list = new ArrayList<>(SIZE);for (int i = 0; i < SIZE; i++) {User user = new User();user.setUsername("user" + i);user.setPassword("password" + i);list.add(user);}long startTime = System.currentTimeMillis();// 批量插入for (User user : list) {userService.insert(user);}long endTime = System.currentTimeMillis();System.out.println("不用线程池===插入40万条记录耗时:" + ((endTime - startTime) / 1000) + "s");}
}

启动项目,测试一下,看要多长时间……11分钟

在这里插入图片描述

使用线程池

Spring Boot有自动注入的线程池(threadPoolTaskExecutor),可以手动设置一些属性,为我们所用。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;@Configuration
@EnableAsync
public class ThreadPoolConfig {@Bean(name = "threadPoolTaskExecutor")public Executor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(20);executor.setMaxPoolSize(40);executor.setQueueCapacity(500);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("hezy-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

使用线程池来完成上面插入数据的操作,如下:

    @GetMapping("insert2")public void insert2() {ArrayList<User> list = new ArrayList<>(SIZE);for (int i = 0; i < SIZE; i++) {User user = new User();user.setUsername("user" + i);user.setPassword("password" + i);list.add(user);}// 将数据分成4000批,每批插入100条List<List<User>> batchList = new ArrayList<>();for (int i = 0; i < list.size(); i += 100) {batchList.add(list.subList(i, i + 100));}long startTime = System.currentTimeMillis();CountDownLatch countDownLatch = new CountDownLatch(batchList.size());// 线程池分批插入for (List<User> batch : batchList) {asyncService.executeAsync(batch, userService, countDownLatch);}try {countDownLatch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}long endTime = System.currentTimeMillis();System.out.println("使用线程池===插入40万条记录耗时:" + ((endTime - startTime) / 1000) + "s");}

AsyncService实现类

import com.hezy.pojo.User;
import com.hezy.service.AsyncService;
import com.hezy.service.UserService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.concurrent.CountDownLatch;@Service
public class AsyncServiceImpl implements AsyncService {@Async("threadPoolTaskExecutor")@Overridepublic void executeAsync(List<User> batch, UserService userService, CountDownLatch countDownLatch) {try {for (User user : batch) {userService.insert(user);}} finally {countDownLatch.countDown();}}
}

启动,测试,速度提升很明显。如果再改造一下insert()方法,一次插入多条数据,肯定还能更快。

在这里插入图片描述

总结

本文介绍如何使用Spring Boot装配的线程池Bean,完成大数据量的批量插入操作,提高程序执行效率。

实例完整代码:https://github.com/HeZhongYing/thread_pool_demo

参考B站UP主(孟哥说Java)视频:https://www.bilibili.com/video/BV18r421F7CQ

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Agent 概念学习

Agent 概念学习 什么是 Agent OpenAI的研究员 Lilian 写过一篇博客:《 LLM Powered Autonomous Agents》&#xff0c;将 Agents 定义为&#xff1a;LLM memory planning skills tool use&#xff0c;即大语言模型、记忆、任务规划、工具使用的集合。 Overview of a LLM-…

多模态—图文匹配

可能最近大家已经发现了chatgpt可以根据自己的描述生成图片&#xff0c;其实这就是一个图文匹配的问题&#xff0c;可以理解为这是一个多模态的问题。 在模型训练时我们需要N个图片和N个文本对进行训练&#xff0c;文本通过text encoder形成文本语义向量&#xff0c;text enco…

930/105每日一题

算法 1 4,2,9,11, 4, 2,4 2,4,9 42 4 24 9 2&#xff08;0&#xff09; 4&#xff08;1&#xff09; 9&#xff08;2&#xff09; 11&#xff08;3&#xff09; 11&#xff08;0&#xff09;11&#xff08;1&#xff09; 9&#xff08;2&#xff09; 11&#xff08;3&#xf…

C++之多态篇(超详细版)

1.多态概念 多态就是多种形态&#xff0c;表示去完成某个行为时&#xff0c;当不同的人去完成时会有不同的形态&#xff0c;举个例子在车站买票&#xff0c;可以分为学生票&#xff0c;普通票&#xff0c;军人票&#xff0c;每种票的价格是不一样的&#xff0c;当你是不同的身…

C语言 | Leetcode C语言题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; int next(int* nums, int numsSize, int cur) {return ((cur nums[cur]) % numsSize numsSize) % numsSize; // 保证返回值在 [0,n) 中 }bool circularArrayLoop(int* nums, int numsSize) {for (int i 0; i < numsSize; i) {if (!n…

C++ | Leetcode C++题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution { public:bool find132pattern(vector<int>& nums) {int n nums.size();vector<int> candidate_i {nums[0]};vector<int> candidate_j {nums[0]};for (int k 1; k < n; k) {auto it_i upper_…

Ubuntu24.04远程开机

近来在几台机器上鼓捣linux桌面&#xff0c;顺便研究一下远程唤醒主机。 本篇介绍Ubuntu系统的远程唤醒&#xff0c;Windows系统的唤醒可搜索相关资料。 依赖 有远程唤醒功能的路由器&#xff08;当前一般都带这个功能&#xff09;有线连接主机&#xff08;无线连接有兴趣朋友…

信息安全工程师(33)访问控制概述

前言 访问控制是信息安全领域中至关重要的一个环节&#xff0c;它提供了一套方法&#xff0c;旨在限制用户对某些信息项或资源的访问权限&#xff0c;从而保护系统和数据的安全。 一、定义与目的 定义&#xff1a;访问控制是给出一套方法&#xff0c;将系统中的所有功能和数据…

【JAVA开源】基于Vue和SpringBoot的宠物咖啡馆平台

本文项目编号 T 064 &#xff0c;文末自助获取源码 \color{red}{T064&#xff0c;文末自助获取源码} T064&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

仿RabbitMQ实现消息队列三种主题的调试及源码

文章目录 开源仓库和项目上线广播交换模式下的测试直接交换模式下的测试主题交换模式下的测试 开源仓库和项目上线 本项目已开源到下面链接下的仓库当中 仿RabbitMQ实现消息队列 广播交换模式下的测试 消费者客户端 在进行不同测试下&#xff0c;消费者客户端只需要改变交换机…

基于SpringBoot+Vue+MySQL的中医院问诊系统

系统展示 用户前台界面 管理员后台界面 医生后台界面 系统背景 随着信息技术的迅猛发展和医疗服务需求的不断增加&#xff0c;传统的中医院问诊流程已经无法满足患者和医院的需求。纸质病历不仅占用大量存储空间&#xff0c;而且容易丢失和损坏&#xff0c;同时难以实现信息的快…

Acwing 背包问题

背包问题 首先&#xff0c;什么是背包问题&#xff1f; 给定N个物品和一个容量为V的背包&#xff0c;每个物品有体积和价值两种属性&#xff0c;在一些限制条件下&#xff0c;将一些物品放入背包&#xff0c;使得在不超过背包体积的情况下&#xff0c;能够得到的最大价值。根据…

【redis学习篇1】redis基本常用命令

目录 redis存储数据的模式 常用基本命令 一、set 二、keys pattern keys 字符串当中携带问号 keys 字符串当中携带*号 keys 【^字母】 keys * 三、exists 四、del 五、expire 5.1 ttl命令 5.2key删除策略 5.2.1惰性删除 5.2.2定期删除 六、type key的数据类型…

Windows安全加固详解

一、补丁管理 使用适当的命令或工具&#xff0c;检查系统中是否有未安装的更新补丁。 Systeminfo 尝试手动安装一个系统更新补丁。 • 下载适当的补丁文件。 • 打开命令提示符或PowerShell&#xff0c;并运行 wusa.exe <patch_file_name>.msu。 二、账号管…

Pikachu-Sql-Inject - 暴力破解

之前的破解&#xff0c;一般都需要 information_schema.schemata 、 information_schema.tables 、information_schema.columns 的权限&#xff0c;如果没有权限&#xff0c;就需要暴力破解&#xff1b; 如构造payload ,这个 abc 表就是我们要确定是否存在的表 vince and ex…

GPTQ vs AWQ vs GGUF(GGML) 速览和 GGUF 文件命名规范

简单介绍一下四者的区别。 参考链接&#xff1a;GPTQ - 2210.17323 | AWQ - 2306.00978 | GGML | GGUF - docs | What is GGUF and GGML? 文章目录 GPTQ vs AWQ vs GGUF&#xff08;GGML&#xff09; 速览GGUF 文件命名GGUF 文件结构文件名解析答案 附录GGUF 文件命名GGUF 文件…

Pandas基础学习

导入 导入pandas一般是这样导入的 import pandas as pdSeries 创建 s1 pd.Series([5, 17, 3, 26, 31])注意Series的第一个字母要大写&#xff0c;表明这其实是Series类的构建函数, 返回的是Series类的实例 获得元素或者索引 单独获得元素 s1.values单独获得索引值 s…

Flink 03 | 数据流基本操作

Flink数据流结构 DataStream 转换 通常我们需要分析的业务数据可能存在如下问题&#xff1a; 数据中包含一些我们不需要的数据 数据格式不方面分析 因此我们需要对原始数据流进行加工&#xff0c;比如过滤、转换等操作才可以进行数据分析。 “ Flink DataStream 转换主要作…

Kubernetes-环境篇-01-mac开发环境搭建

1、brew安装 参考知乎文章&#xff1a;https://zhuanlan.zhihu.com/p/111014448 苹果电脑 常规安装脚本&#xff08;推荐 完全体 几分钟安装完成&#xff09; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"苹果电脑 极…

【Android】中级控件

其他布局 相对布局RelativeLayout RelativeLayout下级视图的位置是相对位置&#xff0c;得有具体的参照物才能确定最终位置。如果不设定下级视图的参照物&#xff0c;那么下级视图默认显示在RelativeLayout内部的左上角。用于确定视图位置的参照物分两种&#xff0c;一种是与…