知识点
ThreadLocal :单线程内可有效访问
InheritableThreadLocal:可在父子线程之间传递
TransmittableThreadLocal:可在父线程与使用 线程池 中的子线程间传递
需求
页面按钮触发一个HTTP操作,该操作包含一个不需要用户的等待结果的耗时操作。所以采用了spring mvc 提供的 @Async 进行异步,该操作使用了默认的线程池,在ThreadLocal中的用户信息,无法进行HTTP请求线程和线程池子线程间的传递。
处理方法
- 替换ThreadLocal为TransmittableThreadLocal
public class UserContextUtils {
private static ThreadLocal<UserDTO> threadLocal = new TransmittableThreadLocal<>();
public static void initDefaultUser() {
threadLocal.set(UserDTO.getDefault());
}
public static void removeCurrentUser() {
threadLocal.remove();
}
public static void setCurrentUser(UserDTO value) {
threadLocal.set(value);
}
public static UserDTO currentUser() {
return threadLocal.get();
}
}
- 使用TtlExecutors包装ThreadPoolExecutor
@Configuration
@EnableAsync
public class AsyncTaskConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat("backend-task-%d").build());
return TtlExecutors.getTtlExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
- 使用 @Async 进行异步方法
@Async
public void readExcelHeader(Integer projectId) {
List<FileUploadInfo> uploadFiles = uploadFileMapper.selectList(new QueryWrapper<FileUploadInfo>().lambda().eq(FileUploadInfo::getProjectId, projectId));
AnProjectInfo originProjectInfo = anProjectInfoMapper.selectById(projectId);
String columns = parserFactory.getParser(originProjectInfo.getType()).readColumns(uploadFiles.get(0));
AnProjectInfo anProjectInfo = new AnProjectInfo();
anProjectInfo.setId(projectId);
anProjectInfo.setColumns(columns);
anProjectInfoMapper.updateById(anProjectInfo);
}