Java多线程——spring-boot 线程池 @Async 的使用、自定义Executor的配置方法
1、在启动类中添加@EnableAsync注解
@Slf4j
@SpringBootApplication
public class TestApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
log.info("TestApplication springboot start success!");
}
}
2、自定义ThreadPoolTaskExecutor
创建一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来,线程任务总数、已完成数、活跃线程数,队列大小等情况一目了然。
/**
* @Description : 自定义扩展Executor
* @Author : SherlockerSun
* @Date : 2022/9/1 17:24
*/
@Slf4j
public class VisibleThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private void showThreadPoolInfo(String prefix){
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if(null==threadPoolExecutor){
return;
}
log.debug("{}, {},taskCount [{}], completedTaskCount [{}],PoolSize [{}],CorePoolSize[{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getPoolSize(),
threadPoolExecutor.getCorePoolSize(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo(task.getClass().getName()+"execute at:" + CommonUtils.formatDate(startTimeout));
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
打印日志工具方法
public class CommonUtils {
/**
* @Description: TODO 格式化json数据,用于打印日志等
* @author: SherlockerSun
* @date 2022年8月26日下午6:53:30
*/
public static String writeFormat(Object obj) {
return JSON.toJSONString(obj, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
}
}
3、新建Executor配置类
/**
* @Description :
* @Author : SherlockerSun
* @Date : 2022/9/1 17:21
*/
@Configuration
@EnableAsync
public class ExecutorConfig {
// 核心线程数
private int corePoolSize = 50;
// 最大线程数
private int maxPoolSize = 500;
// 阻塞队列容量
private int queueCapacity = 400;
@Bean("mySimpleAsync")
public Executor mySimpleAsync() {
//使用自定义ThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new VisibleThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("thread-simple-");
// 这一步是关键,异步Task装饰器
executor.setTaskDecorator(new MyContextDecorator());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Bean("myAsync")
public Executor myAsync() {
ThreadPoolTaskExecutor executor = new VisibleThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("thread-work-");
// 这一步是关键,异步Task装饰器
executor.setTaskDecorator(new MyContextDecorator());
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
这里定义了两个不同的Executor,第二个重新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。
异步Task装饰器MyContextDecorator
public class MyContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
// TODO Auto-generated method stub
// 获取主线程中的请求信息(我们的用户信息也放在里面)
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return () -> {
try {
// 将主线程的请求信息,设置到子线程中
RequestContextHolder.setRequestAttributes(attributes);
// 执行子线程,这一步不要忘了
runnable.run();
} finally {
// 线程结束,清空这些信息,否则可能造成内存泄漏
RequestContextHolder.resetRequestAttributes();
}
};
}
}
4、创建一个AsyncTask类
在里面添加三个用@Async
注解的task
实际项目中这里处理业务,通过future.get()方法获取线程处理的返回结果
@Slf4j
@Component
public class AsyncTask {
@Async("mySimpleAsync")
public Future<String> doTask1() throws InterruptedException{
log.info("mySimpleAsync_Task1 started.");
long start = System.currentTimeMillis();
Thread.sleep(5000);
long end = System.currentTimeMillis();
log.info("mySimpleAsync_Task1 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("mySimpleAsync_Task1 accomplished!");
}
@Async("myAsync")
public Future<String> doTask2() throws InterruptedException{
log.info("myAsync_Task2 started.");
long start = System.currentTimeMillis();
Thread.sleep(3000);
long end = System.currentTimeMillis();
log.info("myAsync_Task2 finished, time elapsed: {} ms.", end-start);
return new AsyncResult<>("myAsync_Task2 accomplished!");
}
}
5、测试类
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TaskTests extends BasicUtClass {
@Autowired
private AsyncTask asyncTask;
@Test
public void AsyncTaskTest() throws InterruptedException, ExecutionException {
Future<String> task1 = asyncTask.doTask1();
Future<String> task2 = asyncTask.doTask2();
while(true) {
if(task1.isDone() && task2.isDone()) {
log.info("Task1 result: {}", task1.get());
log.info("Task2 result: {}", task2.get());
break;
}
Thread.sleep(1000);
}
log.info("All tasks finished.");
}
}
结果
2022-09-02 14:12:30.074 INFO 15812 --- [ thread-work-1] com.juc.config.AsyncTask : myAsync_Task2 started.
2022-09-02 14:12:30.074 INFO 15812 --- [thread-simple-1] com.juc.config.AsyncTask : mySimpleAsync_Task1 started.
2022-09-02 14:12:33.086 INFO 15812 --- [ thread-work-1] com.juc.config.AsyncTask : myAsync_Task2 finished, time elapsed: 3012 ms.
2022-09-02 14:12:35.081 INFO 15812 --- [thread-simple-1] com.juc.config.AsyncTask : mySimpleAsync_Task1 finished, time elapsed: 5007 ms.
2022-09-02 14:12:35.127 INFO 15812 --- [ main] com.juc.TaskTests : Task1 result: mySimpleAsync_Task1 accomplished!
2022-09-02 14:12:35.127 INFO 15812 --- [ main] com.juc.TaskTests : Task2 result: myAsync_Task2 accomplished!
2022-09-02 14:12:35.127 INFO 15812 --- [ main] com.juc.TaskTests : All tasks finished.