背景介绍
1,最近有一个大数据量插入的操作入库的业务场景,需要先做一些其他修改操作,然后在执行插入操作,由于插入数据可能会很多,用到多线程去拆分数据并行处理来提高响应时间,如果有一个线程执行失败,则全部回滚。
2,在spring中可以使用@Transactional注解去控制事务,使出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚,导致数据错误。
3,下面用一个简单示例演示多线程事务。
公用的类和方法
- /**
- * 平均拆分list方法.
- * @param source
- * @param n
- * @param <T>
- * @return
- */
- public static <T> List<List<T>> averageAssign(List<T> source,int n){
- List<List<T>> result=new ArrayList<List<T>>();
- int remaider=source.size()%n;
- int number=source.size()/n;
- int offset=0;//偏移量
- for(int i=0;i<n;i++){
- List<T> value=null;
- if(remaider>0){
- value=source.subList(i*number+offset, (i+1)*number+offset+1);
- remaider--;
- offset++;
- }else{
- value=source.subList(i*number+offset, (i+1)*number+offset);
- }
- result.add(value);
- }
- return result;
- }
- /** 线程池配置
- * @version V1.0
- */
- public class ExecutorConfig {
- private static int maxPoolSize = Runtime.getRuntime().availableProcessors();
- private volatile static ExecutorService executorService;
- public static ExecutorService getThreadPool() {
- if (executorService == null){
- synchronized (ExecutorConfig.class){
- if (executorService == null){
- executorService = newThreadPool();
- }
- }
- }
- return executorService;
- }
- private static ExecutorService newThreadPool(){
- int queueSize = 500;
- int corePool = Math.min(5, maxPoolSize);
- return new ThreadPoolExecutor(corePool, maxPoolSize, 10000L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>(queueSize),new ThreadPoolExecutor.AbortPolicy());
- }
- private ExecutorConfig(){}
- }
- /** 获取sqlSession
- * @author 86182
- * @version V1.0
- */
- @Component
- public class SqlContext {
- @Resource
- private SqlSessionTemplate sqlSessionTemplate;
- public SqlSession getSqlSession(){
- SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
- return sqlSessionFactory.openSession();
- }
- }
复制代码 推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice
示例事务不成功操作
[code] /** * 测试多线程事务. * @param employeeDOList */@Override@Transactionalpublic void saveThread(List employeeDOList) { try { //先做删除操作,如果子线程出现异常,此操作不会回滚 this.getBaseMapper().delete(null); //获取线程池 ExecutorService service = ExecutorConfig.getThreadPool(); //拆分数据,拆分5份 List lists=averageAssign(employeeDOList, 5); //执行的线程 Thread []threadArray = new Thread[lists.size()]; //监控子线程执行完毕,再执行主线程,要不然会导致主线程关闭,子线程也会随着关闭 CountDownLatch countDownLatch = new CountDownLatch(lists.size()); AtomicBoolean atomicBoolean = new AtomicBoolean(true); for (int i =0;i { try { //最后一个线程抛出异常 if (!atomicBoolean.get()){ throw new ServiceException("001","出现异常"); } //批量添加,mybatisPlus中自带的batch方法 this.saveBatch(list); }finally { countDownLatch.countDown(); } }); } for (int i = 0; i |