背景

在spring中 @Transactional 注解可以控制事务,使出现异常时会进行回滚。但在多线程中则不生效。例如现在需要插入大量业务数据,但在插入数据之前主线程先执行删除动作,之后再将数据等分分批交由子线程去执行。那么当其中某个子线程执行失败时,主线程删除的数据则不会回滚,从而导致数据一致性受损。因此需要一个解决方案,如下:

代码

ExecutorConfig

线程池配置类

package com.python.config;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* 线程池配置
*/
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<Runnable>(queueSize), new ThreadPoolExecutor.AbortPolicy());
}

private ExecutorConfig() { }
}

SqlContext

获取sqlSession

package com.python.config;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class SqlContext {
@Resource
private SqlSessionTemplate sqlSessionTemplate;

public SqlSession getSqlSession() {
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
return sqlSessionFactory.openSession();
}
}

多线程插入操作

@Resource
SqlContext sqlContext;

/**
* 测试多线程事务
*/
public void saveThread(List<EmployeeDO> employeeDOList) throws SQLException {
// 获取数据库连接,获取会话(内部自有事务)
SqlSession sqlSession = sqlContext.getSqlSession();
Connection connection = sqlSession.getConnection();
try {
// 设置手动提交
connection.setAutoCommit(false);
//获取mapper
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
//先做删除操作
employeeMapper.delete(null);

// 获取执行器
ExecutorService service = ExecutorConfig.getThreadPool();
List<Callable<Integer>> callableList = new ArrayList<Callable<Integer>>();
// 拆分list
List<List<EmployeeDO>> lists = averageAssign(employeeDOList, 5);
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
for (int i = 0; i < lists.size(); i++) {
List<EmployeeDO> list = lists.get(i);
Callable<Integer> callable = () -> employeeMapper.saveBatch(list);
callableList.add(callable);
}

//执行子线程
List<Future<Integer>> futures = service.invokeAll(callableList);
for (Future<Integer> future : futures) {
//如果有一个执行不成功,则全部回滚
if (future.get() <= 0) {
connection.rollback();
return;
}
}
connection.commit();
log.info("添加完毕");
} catch (Exception e) {
connection.rollback();
log.error("error", e);
throw new ServiceException("001", "出现异常");
} finally {
connection.close();
}
}

/**
* 平均拆分list
*/
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;
}