在执行一个异步任务或并发任务时,往往是通过直接new Thread()方法来创建新的线程,这样做弊端较多,更好的解决方案是合理地利用线程池,使用线程池可以降低系统资源消耗、提高系统响应速度、方便线程并发数的管控。
线程池代码示例
@Configuration
@EnableAsync
public class ThreadPoolManager {
Logger logger = LoggerFactory.getLogger(ThreadPoolManager.class);
/**
* testAsyncTaskExecutor线程池
* */
@Bean("testAsyncTaskExecutor")
public ThreadPoolTaskExecutor asynRecalculateExcutor(){
logger.info("testAsyncTaskExecutor异步线程池开启");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数
executor.setCorePoolSize(50);
//最大线程数
executor.setMaxPoolSize(200);
//核心线程数之外的线程存活时间
executor.setKeepAliveSeconds(60*2);
//队列大小
executor.setQueueCapacity(1500);
//线程名称前缀
executor.setThreadNamePrefix("test_Async_Task_Executor_");
//异步重算线程池修饰器。
executor.setTaskDecorator(new ContextCopyingDecorator());
//当任务完成后,长时间无待处理任务时,销毁线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
/**
* 线程池修饰类
* 注意线程池中有的线程是一直存在一直被复用的,
* 所以线程执行完成后需要在TaskDecorator的finally方法中移除传递的上下文对象,否则就存在内存泄漏的问题。
* TaskDecorator是一个执行回调方法的装饰器,主要应用于传递上下文,或者提供任务的监控/统计信息。
*/
public class ContextCopyingDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
try {
RequestAttributes context = RequestContextHolder.currentRequestAttributes();
Map<String,String> previous = MDC.getCopyOfContextMap();
SecurityContext securityContext = SecurityContextHolder.getContext();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
MDC.setContextMap(previous);
SecurityContextHolder.setContext(securityContext);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
MDC.clear();
SecurityContextHolder.clearContext();
}
};
} catch (IllegalStateException e) {
return runnable;
}
}
}
}
如何使用?
1、注入bean使用
@Resource(name = "testAsyncTaskExecutor")
ThreadPoolTaskExecutor executor;
2、@Async注解异步方法
//在项目中, 偶尔需要使用异步的方式去执行任务.所以,我们可以引入多线程的使用,SpringBoot中支持多线程,使用@EnableAsync注解就可以使用多线程了,@Async放在需要异步执行的方法上,非常简单方便的使用多线程去完成任务.
@Async("testAsyncTaskExecutor")
@Override
public void test() {
//代码
}
执行execute()方法和submit()方法的区别是什么呢
1、execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
2、submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
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);
}
}
@Override
public void execute(Runnable task, long startTimeout) {
execute(task);
}
@Override
public Future<?> submit(Runnable task) {
ExecutorService executor = getThreadPoolExecutor();
try {
return executor.submit(task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}