一、前言
今天遇到个bug,之前做的多线程接口一直报空,看了下代码发现配置的线程数超过了线程池最大线程数限制了,解决之后正好借这个机会来回顾记录下多线程操作。
注意:多线程接口因为是在旧系统中改造,既有hibernate框架已经做好封装,为了支持多线程选择用多线程发起请求调用单独业务接口处理。同理对于数据状态修改放到业务接口处理。
二、代码
业务处理接口不再贴出,避免出现幂等以及脏数据。关键代码如下,实体类已改成测试用户类,自己调整即可。
调用接口
@Autowired
private SysUserService sysUserService;
@Autowired
private RkBatchPoolTest rkBatchPoolTest;
@RequestMapping(value = "/projectRKBatch", method = RequestMethod.POST)
public @ResponseBody SysResult projectRKBatch(@RequestParam("size") String size,@RequestParam(value = "type",required = false) String type) {
log.info("————————————————————开始批量入库——————————————————————");
if (StringUtils.isBlank(size)) {
size = "1000";
}
if (StringUtils.isBlank(type)) {
type = "NEW";
}
//查询出对应数据
List<SysUser> userList = this.sysUserService.getUserListBySize(Integer.valueOf(size));
log.info("批量入库条数" + userList.size());
try {
if ("NEW".equals(type)) {
String rKBatchThreadUrl ="http//:localhost:8099/projectRK";//this.getRKBatchThreadUrl();
return rkBatchPoolTest.rkBatchInit(userList,rKBatchThreadUrl);
} else {
//旧逻辑不在记录
return SysResult.success();
}
} catch (Exception e) {
log.error("批量入库异常:{},{}",e.getMessage(),e.getCause());
return SysResult.fail(e.getMessage(),e.getCause());
}
}
RkBatchPoolTest
import com.supermap.base.core.JsonMessage;
import com.supermap.dbserver.model.LSBCB1303;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @Author zeh.su
* @Date 2024-07-29 14:20
* @Description
**/
@Component
public class RkBatchPoolTest {
@Resource
private RestTemplateConfig restTemplateConfig;
private static final Logger logger = LoggerFactory.getLogger(RkBatchPoolTest.class);
@Value("${thread.maxThread}")
private int maxThread; // 单任务中最大线程数
//均分 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;
}
public JsonMessage rkBatchInit(List<LSBCB1303> bwids, String rKBatchThreadUrl) throws ExecutionException, InterruptedException {
Integer coreSize = maxThread;
logger.info("批量入库线程数:{}",coreSize);
logger.info("批量入库最大线程数:{}","48");
JsonMessage msg = new JsonMessage();
try {
List<List<LSBCB1303>> lists = averageAssign(bwids, coreSize);
ExecutorService pool = new ThreadPoolExecutor(coreSize, 48, 6, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
List<RkCallable> callableList = new ArrayList<>();
for (Integer i = 0; i < coreSize; i++) {
RkCallable rkCallable = new RkCallable(lists.get(i), restTemplateConfig, rKBatchThreadUrl);
callableList.add(rkCallable);
}
List<Future<JsonMessage>> futures = pool.invokeAll(callableList);
for (Future<JsonMessage> future : futures) {
logger.info("批量入库结果:",future.get().toString());
}
} catch (Exception e) {
logger.error("批量入库线程初始化异常:" + e.getMessage());
logger.error("批量入库线程初始化异常:" + e.getCause());
msg.setState(false);
msg.setMsg("批量入库线程初始化异常"+e.getMessage());
}
msg.setState(true);
msg.setMsg("批量入库完毕");
return msg;
}
}
RkCallable
package com.hua.utils;
import com.hua.config.RestTemplateConfig;
import com.hua.entity.SysUser;
import com.hua.vo.SysResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
class RkCallable implements Callable<SysResult> {
private static final Logger logger = LoggerFactory.getLogger(RkCallable.class);
//无法注解注入
RestTemplateConfig restTemplateConfig;
private List<SysUser> n;
String rKBatchThreadUrl;
public RkCallable(List<SysUser> n, RestTemplateConfig config, String url) {
this.n = n;
this.restTemplateConfig = config;
this.rKBatchThreadUrl = url;
}
@Override
public SysResult call() throws Exception {
try {
return rkBatch(n, rKBatchThreadUrl);
} catch (Exception e) {
logger.error(e.getMessage());
System.out.println(Thread.currentThread().getName() + e.getMessage());
return SysResult.fail(e.getMessage(),e.getCause());
}
}
public SysResult rkBatch(List<SysUser> dataList, String rKBatchThreadUrl) throws Exception {
logger.info(Thread.currentThread().getName() + "开始执行批量入库任务 数量:" + dataList.size());
RestTemplate restTemplate = restTemplateConfig.restTemplate();
SysResult rkRes;
for (SysUser data : dataList) {
try {
ResponseEntity<SysResult> resultResponseEntit = restTemplate.postForEntity(rKBatchThreadUrl, data, SysResult.class);
rkRes = resultResponseEntit.getBody();
if (200 == rkRes.getStatus()) {
System.out.println(data.getId() + ":入库成功");
} else {
System.out.println(data.getId() + ":入库失败");
}
} catch (Exception e) {
logger.error(e.getMessage());
System.out.println(Thread.currentThread().getName() + e.getMessage());
continue;
}
// 备用提升性能
Thread.sleep(500);
}
logger.info(Thread.currentThread().getName() + "批量入库任务结束" + new Date().toString());
System.out.println(Thread.currentThread().getName() + "批量入库任务结束" + new Date().toString());
return SysResult.success();
}
}