异步执行和异步提升相应性能
异步任务和异步提升相应性能
区别
1、异步任务
异步任务是Controller接收到请求后,主线程直接返回结果,开启一个新的线程来处理请求。
这里异步任务能有效提高Controller的吞吐量,因为Tomcat的Controller线程是有限的,所以把Controller线程提前释放掉,同时可以并行执行任务。
这里异步任务最大的问题在于处理后的返回值不能返回给客户端。
2、异步提升相应性能
异步提升响应性能,以下成为异步响应,和异步任务最大的区别在于异步响应会将处理后的返回值返回给客户端。
异步响应在Controller接受了请求后,将处理请求和相应的任务都交给子线程。
实现方式
异步任务
1、在方法上加上@Async注解
@Component
@Slf4j
public class LongTimeTask {
public void task1() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("done");
}
@Async
public void task2() {
task1();
}
}
2、在主类上加上@EnableAsync
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
线程池配置
默认配置
如果没有任何配置,那么spring默认采用的是SimpleAsyncTaskExecutor。
SimpleAsyncTaskExecutor,不算是一个真正的线程池,因为没有对线程进行复用。
每次执行客户提交给它的任务时,它会启动新的线程,并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用。默认时,concurrencyLimit取值为-1,即不启用资源节流。
https://blog.csdn.net/u012881904/article/details/78142991?utm_source=blogxgwz9
自定义线程池
@Slf4j
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Value("${thread.pool.corePoolSize:10}")
private int corePoolSize;
@Value("${thread.pool.maxPoolSize:20}")
private int maxPoolSize;
@Value("${thread.pool.keepAliveSeconds:4}")
private int keepAliveSeconds;
@Value("${thread.pool.queueCapacity:512}")
private int queueCapacity;
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);//当前线程数
executor.setMaxPoolSize(maxPoolSize);// 最大线程数
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setQueueCapacity(queueCapacity);//线程池所使用的缓冲队列
executor.setThreadNamePrefix("MyAsync-");// 线程名称前缀
executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor exe) -> {
log.warn("当前任务线程池队列已满.");
});//拒绝策略
log.info("--------------------------》》》开启异常线程池");
executor.initialize();
return executor;
}
/**
* 自定义异常处理类
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.error("线程池执行任务发生未知异常.", ex);
}
};
}
}
异步响应
Callable
Callable是被主线程开启,然后执行
@GetMapping("task_call")
public Callable task2() {
log.info("Request received");
Callable<String> callable = appService::execute;
log.info("Servlet thread released");
return callable;
}
DeferredResult
DeferredResult和callable最大的区别,在于DeferredResult可以由其他线程来赋值返回,可以多个线程来操作,主线程只需要创建一个DeferredResult
适用场景
/**
* 只需要创建DeferredResult
*
* @return
* @throws InterruptedException
*/
@GetMapping("order")
public DeferredResult<String> get() throws InterruptedException {
String orderNum = "xxx";
mockQueue.setPlaceOrder(orderNum);
//10s超时
DeferredResult<String> result = new DeferredResult<>(10 * 1000L);
deferredResultHolder.getMap().put(orderNum, result);
return result;
}
/**
* 监听spring初始化完毕后的事件
* ApplicationListener<ContextRefreshedEvent>
*/
@Component
@Slf4j
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
/**
* 监听来DeferredResult中setResult,setResult后主线程完成相应
*
* @param contextRefreshedEvent
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
new Thread(() -> {
while (true) {
if (!StringUtils.isEmpty(mockQueue.getCompleteOrder())) {
String orderNum = mockQueue.getCompleteOrder();
log.info("放回订单处理结果" + orderNum);
deferredResultHolder.getMap().get(orderNum).setResult("place hold success");
mockQueue.setCompleteOrder(null);
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}