1.项目中多线程的使用
1.1 多线程代码
public class PreLoanController extends BaseController {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String SUCCESS_CODE = "0000";//返回成功码
private static final String FAILURE_CODE = "9999";//返回失败码
@Value("${key_aes_apply}")
private String AES_KEY;//进件AES密钥
@Autowired
private PreLoanService preLoanService;
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
/**
* 预审接口
*
* @throws Exception
*/
@RequestMapping(value = "/apply", method = RequestMethod.POST)
public @ResponseBody JSONObject apply(HttpServletRequest request, HttpServletResponse response){
String plainContent = "";
try {
plainContent = request.getParameter("plainContent");
if(StringUtils.isBlank(plainContent)){
return ResponseVO.failure(FAILURE_CODE,"请求参数不能为空");
}
//自测不加密----后期删除该代码
String type = request.getParameter("type");
if(StringUtils.isBlank(type)){
plainContent = AESUtil.decrypt(plainContent, AES_KEY);
}
logger.info("解密后参数plainContent:{}", plainContent);
if(StringUtils.isBlank(plainContent)){
return ResponseVO.failure(FAILURE_CODE,"请求参数解决异常");
}
PreLoanApply apply = JSON.parseObject(plainContent, PreLoanApply.class);
String strRequestNo = apply.getStrRequestNo();//请求编号
if(StringUtils.isBlank(strRequestNo) || StringUtils.isBlank(apply.getFlowNo())) {
return ResponseVO.failure(FAILURE_CODE,"请求号或流程编号不能为空");
}
int count = preLoanService.countByRequestNo(strRequestNo);
if(count > 0){
logger.info("订单号["+strRequestNo+"]已经存在,不能重复进件");
return ResponseVO.failure(FAILURE_CODE,"订单["+strRequestNo+"]已经存在,不能重复进件");
}
ApplyJson info = new ApplyJson();
info.setId(GeneratorUUID.getId());
info.setCreateDate(DateUtils.formatDate(new Date(),"yyyy-MM-dd HH:mm:ss"));
info.setStrRequest(plainContent);
info.setStrRequestNo(apply.getStrRequestNo());
info.setCustomerno(apply.getBaseInfo().getCustomerno());
info.setType("3");
preLoanService.saveApplyJSON(info);
//基础参数校验
ValidationResult validationResult = ValidationUtils.validateEntity(apply.getBaseInfo());
if (validationResult.isHasErrors()) {
Map<String, String> errorMsg = validationResult.getErrorMsg();
logger.info("参数校验不通过[{}]:{}",strRequestNo,errorMsg);
return ResponseVO.failure(FAILURE_CODE,"参数校验不通过["+errorMsg+"]");
}
//开启多线程处理数据
taskExecutor.execute(new Runnable() {
@Override
public void run() {
JSONObject res = handle(apply);
logger.info("进件结果[{}]:{}", strRequestNo,res);
}
});
return ResponseVO.success(SUCCESS_CODE);
} catch (Exception e) {
e.printStackTrace();
logger.error("Fail = " + e.getMessage());
return ResponseVO.failure(FAILURE_CODE,"请求异常["+e.getMessage()+"]");
}
}
}
1.2.线程池的配置
spring-context-thread.xml的配置如下:
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数 -->
<property name="corePoolSize" value="${task.core_pool_size}" />
<!-- 最大线程数 -->
<property name="maxPoolSize" value="${task.max_pool_size}" />
<!-- 队列最大长度 -->
<property name="queueCapacity" value="${task.queue_capacity}" />
<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="${task.keep_alive_seconds}" />
</bean>
pom.xml文件添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.2.6.RELEASE</version>
</dependency>
1.3 ThreadPoolTaskExecutor的参数:
int corePoolSize:线程池维护线程的最小数量.
int maximumPoolSize:线程池维护线程的最大数量.
long keepAliveTime:空闲线程的存活时间.
TimeUnit unit: 时间单位,现有纳秒,微秒,毫秒,秒枚举值.
BlockingQueue<Runnable> workQueue:持有等待执行的任务队列.
RejectedExecutionHandler handler: 用来拒绝一个任务的执行,有两种情况会发生这种情况。
一是在execute方法中若addIfUnderMaximumPoolSize(command)为false,即线程池已经饱和;
二是在execute方法中, 发现runState!=RUNNING || poolSize == 0,即已经shutdown,就调用ensureQueuedTaskHandled(Runnable command),在该方法中有可能调用reject。
1.4 处理流程
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
2 线程池源码解析
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类
@Override
public void execute(Runnable task) {
//获取线程池执行对象
Executor executor = getThreadPoolExecutor();
try {
//执行多线程任务
executor.execute(task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}
public ThreadPoolExecutor getThreadPoolExecutor() throws IllegalStateException {
Assert.state(this.threadPoolExecutor != null, "ThreadPoolTaskExecutor not initialized");
return this.threadPoolExecutor;
}
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
//创建阻塞队列
BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
//创建线程池执行对象
ThreadPoolExecutor executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
}
this.threadPoolExecutor = executor;
return executor;
}
//LinkedBlockingQueue SynchronousQueue是BlockingQueue的实现类
protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
//如果队列容量未满,创建LinkedBlockingQueue对象
if (queueCapacity > 0) {
return new LinkedBlockingQueue<Runnable>(queueCapacity);
}
//如果队列容量已满,创建SynchronousQueue对象
else {
return new SynchronousQueue<Runnable>();
}
}
TreadPoolExecutor的执行参考https://www.cnblogs.com/dolphin0520/p/3932921.html