Spring Boot 自定义线程池实现异步开发相信看过陈某的文章都了解,但是在实际开发中需要在父子线程之间传递一些数据,比如用户信息,链路信息等等
比如用户登录信息使用ThreadLocal存放保证线程隔离,代码如下:
/** * @author 公众号:码猿技术专栏 * @description 用户上下文信息 */ public class OauthContext { private static final ThreadLocal<LoginVal> loginValThreadLocal=new ThreadLocal<>(); public static LoginVal get(){ return loginValThreadLocal.get(); } public static void set(LoginVal loginVal){ loginValThreadLocal.set(loginVal); } public static void clear(){ loginValThreadLocal.remove(); } }
那么子线程想要获取这个LoginVal如何做呢?
今天就来介绍几种优雅的方式实现Spring Boot 内部的父子线程的数据传递。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
1. 手动设置
每执行一次异步线程都要分为两步:
-
获取父线程的LoginVal
-
将LoginVal设置到子线程,达到复用
代码如下:
public void handlerAsync() { //1. 获取父线程的loginVal LoginVal loginVal = OauthContext.get(); log.info("父线程的值:{}",OauthContext.get()); CompletableFuture.runAsync(()->{ //2. 设置子线程的值,复用 OauthContext.set(loginVal); log.info("子线程的值:{}",OauthContext.get()); }); }
虽然能够实现目的,但是每次开异步线程都需要手动设置,重复代码太多,看了头疼,你认为优雅吗?
2. 线程池设置TaskDecorator
TaskDecorator是什么?官方api的大致意思:这是一个执行回调方法的装饰器,主要应用于传递上下文,或者提供任务的监控/统计信息。
知道有这么一个东西,如何去使用?
TaskDecorator是一个接口,首先需要去实现它,代码如下:
/** * @author 公众号:码猿技术专栏 * @description 上下文装饰器 */ public class ContextTaskDecorator implements TaskDecorator { @Override public Runnable decorate(Runnable runnable) { //获取父线程的loginVal LoginVal loginVal = OauthContext.get(); return () -> { try { // 将主线程的请求信息,设置到子线程中 OauthContext.set(loginVal); // 执行子线程,这一步不要忘了 runnable.run(); } finally { // 线程结束,清空这些信息,否则可能造成内存泄漏 OauthContext.clear(); } }; } }
这里我只是设置了LoginVal,实际开发中其他的共享数据,比如SecurityContext,RequestAttributes....
TaskDecorator需要结合线程池使用,实际开发中异步线程建议使用线程池,只需要在对应的线程池配置一下,代码如下:
@Bean("taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor(); poolTaskExecutor.setCorePoolSize(xx); poolTaskExecutor.setMaxPoolSize(xx); // 设置线程活跃时间(秒) poolTaskExecutor.setKeepAliveSeconds(xx); // 设置队列容量 poolTaskExecutor.setQueueCapacity(xx); //设置TaskDecorator,用于解决父子线程间的数据复用 poolTaskExecutor.setTaskDecorator(new ContextTaskDecorator()); poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任务结束后再关闭线程池 poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); return poolTaskExecutor; }
此时业务代码就不需