简而言之:接口中部份业务逻辑可以通过并行的方式来优化接口性能
@Configuration
@EnableAsync
public class TaskPoolConfig {
@Bean("taskExecutor") // bean 的名称,默认为首字母小写的方法名
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数(CPU核心数+1)
executor.setCorePoolSize(10);
//最大线程数(2*CPU核心数+1)
executor.setMaxPoolSize(20);
//缓冲队列数
executor.setQueueCapacity(200);
//允许线程空闲时间(单位:默认为秒)
executor.setKeepAliveSeconds(60);
//线程池名前缀
executor.setThreadNamePrefix("sub-thread-");
// 增加 TaskDecorator 属性的配置
executor.setTaskDecorator(new ContextDecorator());
// 线程池对拒绝任务的处理策略:CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
1.实现TaskDecorator接口
/**
* 子线程装饰器
*
* @author Da Shuai
* @date 2021-06-10 18:28:17
*/
public class SubThreadTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
2.之前的线程池配置加如下代码使其生效
// 增加 TaskDecorator 属性的配置
executor.setTaskDecorator(new ContextDecorator());
1.CountDownLatch
思路:
这样线程计数器为0之前,主线程就一直处于pending状态
主线程逻辑
new CountDownLatch(X)
latch.await()
@Override
@Transactional
public void importExcel(File file) {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
VoteDO voteDO = new VoteDO();
voteDO.setTitle(i + "");
asyncManager.asyncSaveVote(voteDO);
}
//System.out.println(1/0);
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
子线程逻辑
latch.countDown()
@Override
@Async
public void asyncSaveVote(VoteDO voteDO, CountDownLatch latch) {
log.info("当前线程为 {},休眠10s开始", Thread.currentThread().getName());
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("当前线程为 {},休眠10s结束", Thread.currentThread().getName());
log.info("当前线程为 {},保存开始", Thread.currentThread().getName());
voteDO.setDesc(Thread.currentThread().getName());
voteDao.insert(voteDO);
latch.countDown();
log.info("当前线程为 {},保存结束", Thread.currentThread().getName());
}
日志
2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : ===============请求内容===============
2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 请求地址:http://localhost:8080/api/import
2021-06-11 16:31:08.653 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 请求方式:POST
2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 请求类方法:com.zhdj.controller.ImportController.importExcel
2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : 请求类方法参数:[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@42c3f403]
2021-06-11 16:31:08.655 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : ===============请求内容===============
2021-06-11 16:31:08.676 INFO 27516 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-06-11 16:31:08.894 INFO 27516 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-3,休眠10s开始
2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-1,休眠10s开始
2021-06-11 16:31:08.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-2,休眠10s开始
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-2,休眠10s结束
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-3,休眠10s结束
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-2,保存开始
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-1,休眠10s结束
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-3,保存开始
2021-06-11 16:31:18.921 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-1,保存开始
2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:31:19.080 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : ==> Parameters: 0(String), sub-thread-1(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : ==> Parameters: 2(String), sub-thread-3(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
2021-06-11 16:31:19.156 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : ==> Parameters: 1(String), sub-thread-2(String), 2021-06-11T16:31:19.032(LocalDateTime), 2021-06-11T16:31:19.037(LocalDateTime)
2021-06-11 16:31:19.172 DEBUG 27516 --- [ sub-thread-3] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:31:19.178 DEBUG 27516 --- [ sub-thread-2] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:31:19.187 DEBUG 27516 --- [ sub-thread-1] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-3] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-3,保存结束
2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-1] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-1,保存结束
2021-06-11 16:31:19.224 INFO 27516 --- [ sub-thread-2] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-2,保存结束
2021-06-11 16:31:19.226 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : --------------返回内容----------------
2021-06-11 16:31:19.328 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : Response内容:null
2021-06-11 16:31:19.328 INFO 27516 --- [nio-8080-exec-1] com.zhdj.config.LogAspect : --------------返回内容----------------
2.Future
思路:
1.子线程逻辑返回Future对象
2.主线程逻辑循环判断每个子线程返回的Future对象isDone()是否为true
主线程逻辑
循环判断future.isDone()是否为true
@Override
@Transactional
public void importExcel(File file) {
List<Future> futureList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
VoteDO voteDO = new VoteDO();
voteDO.setTitle(i + "");
Future future = asyncManager.asyncSaveVote(voteDO);
futureList.add(future);
}
//检查所有子线程是否均执行完毕
while (true) {
boolean isAllDone = true;
for (Future future : futureList) {
if (null == future || !future.isDone()) {
isAllDone = false;
}
}
if (isAllDone) {
log.info("所有子线程执行完毕");
break;
}
}
}
子线程逻辑
返回Future对象
@Override
public Future asyncSaveVote(VoteDO voteDO) {
log.info("当前线程为 {},休眠10s开始", Thread.currentThread().getName());
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("当前线程为 {},休眠10s结束", Thread.currentThread().getName());
log.info("当前线程为 {},保存开始", Thread.currentThread().getName());
voteDO.setDesc(Thread.currentThread().getName());
voteDao.insert(voteDO);
log.info("当前线程为 {},保存结束", Thread.currentThread().getName());
//返回需要用AsyncResult类
return new AsyncResult<>(true);
}
日志
2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : ===============请求内容===============
2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 请求地址:http://localhost:8080/api/import
2021-06-11 16:42:28.974 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 请求方式:POST
2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 请求类方法:com.zhdj.controller.ImportController.importExcel
2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : 请求类方法参数:[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@7e23bacc]
2021-06-11 16:42:28.975 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : ===============请求内容===============
2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-5,休眠10s开始
2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-4,休眠10s开始
2021-06-11 16:42:28.979 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-6,休眠10s开始
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-6,休眠10s结束
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-4,休眠10s结束
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-5,休眠10s结束
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-6,保存开始
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-5,保存开始
2021-06-11 16:42:38.980 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-4,保存开始
2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:42:38.981 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : ==> Preparing: INSERT INTO vote ( title, `desc`, gmt_create, gmt_modified ) VALUES ( ?, ?, ?, ? )
2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : ==> Parameters: 1(String), sub-thread-5(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : ==> Parameters: 0(String), sub-thread-4(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
2021-06-11 16:42:38.982 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : ==> Parameters: 2(String), sub-thread-6(String), 2021-06-11T16:42:38.980(LocalDateTime), 2021-06-11T16:42:38.981(LocalDateTime)
2021-06-11 16:42:38.988 DEBUG 20492 --- [ sub-thread-5] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:42:38.989 INFO 20492 --- [ sub-thread-5] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-5,保存结束
2021-06-11 16:42:38.993 DEBUG 20492 --- [ sub-thread-6] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:42:38.993 INFO 20492 --- [ sub-thread-6] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-6,保存结束
2021-06-11 16:42:39.004 DEBUG 20492 --- [ sub-thread-4] com.zhdj.dao.VoteDao.insert : <== Updates: 1
2021-06-11 16:42:39.005 INFO 20492 --- [ sub-thread-4] com.zhdj.AsyncManagerImpl : 当前线程为 sub-thread-4,保存结束
2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.service.impl.VoteServiceImpl : 所有子线程执行完毕
2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : --------------返回内容----------------
2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : Response内容:null
2021-06-11 16:42:39.005 INFO 20492 --- [nio-8080-exec-2] com.zhdj.config.LogAspect : --------------返回内容----------------