SpringBoot中使用多线程ThreadPoolTaskExecutor+CompletableFuture
SpringBoot中使用多线程ThreadPoolTaskExecutor
+CompletableFuture
定义一个线程池,并将其注入为bean
我使用的是spring提供的线程池,所以不需要写关闭的逻辑
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;
@Configuration
@EnableAsync
public class ExecutorConfig {
@Bean(name = {"saveExecutor"})
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);//核心线程数
executor.setMaxPoolSize(1000);// 最大线程数
executor.setQueueCapacity(500);// 任务队列容量
executor.setThreadNamePrefix("taskExecutor-");
executor.initialize(); //初始化线程池
return executor;
}
}
使用
import cn.hutool.core.collection.ListUtil;
import com.train.demo.entities.User;
import com.train.demo.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@Service
@Slf4j
public class UserService {
private final UserRepository userRepository;
private final Executor saveExecutor;
@Autowired
public UserService(UserRepository userRepository,
// 依赖注入,一定要使用@Qualifier指定bean名称,不然有问题
@Qualifier("saveExecutor") Executor saveExecutor) {
this.userRepository = userRepository;
this.saveExecutor = saveExecutor;
}
public boolean addAllUsers() {
// 定义一个数组,并生成1000000条数据
ArrayList<User> users = new ArrayList<User>(){{
for (int i = 0; i < 1000000; i++) {
add(new User(null,"user"+i,2,"男"));
}
}};
int count = 0;
// 定义一个接收“CompletableFuture返回”的list
List<CompletableFuture<Integer>> futureList = new ArrayList<>();
while( count < users.size() ){
// 这里只是计算剩下的数据量够不够1000,结果作为截取list的参数
int size = users.subList( count, users.size()-1).size() > 9999 ? 9999
: users.subList( count, users.size()-1).size();
// 调用异步方法
CompletableFuture<Integer> future = addAllUsersImpl(users.subList(count, count += size));
futureList.add(future);
count++;
}
futureList.forEach(CompletableFuture::join);//阻塞所有的异步方法全执行完毕再向下执行
return true;
}
@Async
protected CompletableFuture<Integer> addAllUsersImpl(List<User> users) {
// 此处使用spring线程池作为CompletableFuture线程池参数,并且异步执行
return CompletableFuture.supplyAsync(() -> {
log.info("save users thread[{}]", Thread.currentThread().getName());
List<User> list = ListUtil.list(false, userRepository.saveAll(users));// 存储到数据库
return list.size();// 返回存储的数据量
}, saveExecutor);
}
}
本地mysql实测,99.9万条数据,耗时27s
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18521698
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现