点击上方“程序员蜗牛g”,选择“设为星标”跟蜗牛哥一起,每天进步一点点
程序员蜗牛g大厂程序员一枚 跟蜗牛一起 每天进步一点点31篇原创内容**公众号
利用 Spring Boot 3.4 结合 ThreadPoolTaskExecutor,使数据插入任务并发执行,提高数据库写入吞吐量。
**线程池配置
**
Spring 线程池 Bean 配
package com.icoderoad.config;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;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@Slf4jpublic class ExecutorConfig { @Value("${async.executor.thread.core_pool_size}") private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}") private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}") private int queueCapacity;
@Value("${async.executor.thread.name.prefix}") private String namePrefix;
@Bean(name = "asyncServiceExecutor") public Executor asyncServiceExecutor() { log.warn("启动线程池 asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setThreadNamePrefix(namePrefix); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; }}
异步任务执行
package com.icoderoad.service.impl;
import com.icoderoad.mapper.LogOutputResultMapper;import com.icoderoad.model.LogOutputResult;import com.icoderoad.service.AsyncService;import lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Service;
import java.util.List;import java.util.concurrent.CountDownLatch;
@Slf4j@Servicepublic class AsyncServiceImpl implements AsyncService { @Override @Async("asyncServiceExecutor") public void executeAsync(List<LogOutputResult> logOutputResults, LogOutputResultMapper logOutputResultMapper, CountDownLatch countDownLatch) { try { log.warn("执行异步插入任务"); logOutputResultMapper.addLogOutputResultBatch(logOutputResults); } finally { countDownLatch.countDown(); } }}
业务调用多线程插入
package com.icoderoad.service.impl;
import com.icoderoad.mapper.LogOutputResultMapper;import com.icoderoad.model.LogOutputResult;import com.icoderoad.service.AsyncService;import com.icoderoad.utils.ConvertHandler;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;
import java.util.List;import java.util.concurrent.CountDownLatch;
@Slf4j@Servicepublic class LogOutputService { private final AsyncService asyncService; private final LogOutputResultMapper logOutputResultMapper;
public LogOutputService(AsyncService asyncService, LogOutputResultMapper logOutputResultMapper) { this.asyncService = asyncService; this.logOutputResultMapper = logOutputResultMapper; }
public int testMultiThread() { List<LogOutputResult> logOutputResults = getTestData(); List<List<LogOutputResult>> lists = ConvertHandler.splitList(logOutputResults, 100); CountDownLatch countDownLatch = new CountDownLatch(lists.size());
for (List<LogOutputResult> listSub : lists) { asyncService.executeAsync(listSub, logOutputResultMapper, countDownLatch); }
try { countDownLatch.await(); } catch (Exception e) { log.error("多线程插入异常: " + e.getMessage()); }
return logOutputResults.size(); }
private List<LogOutputResult> getTestData() { return ConvertHandler.generateTestData(3000000); }}
工具类 ConvertHandler
package com.icoderoad.utils;
import com.icoderoad.model.LogOutputResult;
import java.util.ArrayList;import java.util.List;import java.util.stream.Collectors;import java.util.stream.IntStream;
public class ConvertHandler { public static <T> List<List<T>> splitList(List<T> list, int size) { List<List<T>> parts = new ArrayList<>(); for (int i = 0; i < list.size(); i += size) { parts.add(new ArrayList<>(list.subList(i, Math.min(list.size(), i + size)))); } return parts; }
public static List<LogOutputResult> generateTestData(int count) { return IntStream.range(0, count) .mapToObj(i -> new LogOutputResult((long) i, "TestLog " + i)) .collect(Collectors.toList()); }}
数据访问层 LogOutputResultMapper
package com.icoderoad.mapper;
import com.icoderoad.model.LogOutputResult;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import java.util.List;
@Mapperpublic interface LogOutputResultMapper { @Insert("INSERT INTO log_output_result (id, message) VALUES (#{id}, #{message})") void addLogOutputResultBatch(List<LogOutputResult> logOutputResults);}
测试结果
- 单线程 插入 300万 数据,耗时 5.75分钟。
- 30个线程 并发插入 300万 数据,耗时 1.67分钟,效率提升 3.4倍!
- 数据完整性检查无误,无重复数据。
结论
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!