Java SpringBoot 线程池
Spring提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskExecutor,直接使用注解启用

使用步骤

先创建一个线程池的配置,让Spring Boot加载,用来定义如何创建一个ThreadPoolTaskExecutor,要使用@Configuration@EnableAsync这两个注解,表示这是个配置类,并且是线程池的配置类

  1. @Configuration
  2. @EnableAsync
  3. public class ExecutorConfig {
  4. private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
  5. @Value("${async.executor.thread.core_pool_size}")
  6. private int corePoolSize;
  7. @Value("${async.executor.thread.max_pool_size}")
  8. private int maxPoolSize;
  9. @Value("${async.executor.thread.queue_capacity}")
  10. private int queueCapacity;
  11. @Value("${async.executor.thread.name.prefix}")
  12. private String namePrefix;
  13. @Bean(name = "asyncServiceExecutor")
  14. public Executor asyncServiceExecutor() {
  15. logger.info("start asyncServiceExecutor");
  16. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  17. //配置核心线程数
  18. executor.setCorePoolSize(corePoolSize);
  19. //配置最大线程数
  20. executor.setMaxPoolSize(maxPoolSize);
  21. //配置队列大小
  22. executor.setQueueCapacity(queueCapacity);
  23. //配置线程池中的线程的名称前缀
  24. executor.setThreadNamePrefix(namePrefix);
  25. // rejection-policy:当pool已经达到max size的时候,如何处理新任务
  26. // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
  27. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  28. //执行初始化
  29. executor.initialize();
  30. return executor;
  31. }
  32. }

@Value是配置在application.properties,可以参考配置,自由定义

  1. # 异步线程配置
  2. # 配置核心线程数
  3. async.executor.thread.core_pool_size = 5
  4. # 配置最大线程数
  5. async.executor.thread.max_pool_size = 5
  6. # 配置队列大小
  7. async.executor.thread.queue_capacity = 99999
  8. # 配置线程池中的线程的名称前缀
  9. async.executor.thread.name.prefix = async-service-

创建一个Service接口,是异步线程的接口

  1. public interface AsyncService {
  2. /**
  3. * 执行异步任务
  4. * 可以根据需求,自己加参数拟定,我这里就做个测试演示
  5. */
  6. void executeAsync();
  7. }

实现类

  1. @Service
  2. public class AsyncServiceImpl implements AsyncService {
  3. private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);
  4. @Override
  5. @Async("asyncServiceExecutor")
  6. public void executeAsync() {
  7. logger.info("start executeAsync");
  8. System.out.println("异步线程要做的事情");
  9. System.out.println("可以在这里执行批量插入等耗时的事情");
  10. logger.info("end executeAsync");
  11. }
  12. }

将Service层的服务异步化,在executeAsync()方法上增加注解@Async("asyncServiceExecutor")asyncServiceExecutor方法是前面ExecutorConfig.java中的方法名,表明executeAsync方法进入的线程池是asyncServiceExecutor方法创建的。
接下来就是在Controller里或者是哪里通过注解@Autowired注入这个Service

  1. @Autowired
  2. private AsyncService asyncService;
  3. @GetMapping("/async")
  4. public void async(){
  5. asyncService.executeAsync();
  6. }

用postman或者其他工具来多次测试请求一下

  1. 2018-07-16 22:15:47.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  2. 异步线程要做的事情
  3. 可以在这里执行批量插入等耗时的事情
  4. 2018-07-16 22:15:47.655 INFO 10516 --- [async-service-5] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  5. 2018-07-16 22:15:47.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  6. 异步线程要做的事情
  7. 可以在这里执行批量插入等耗时的事情
  8. 2018-07-16 22:15:47.770 INFO 10516 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  9. 2018-07-16 22:15:47.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  10. 异步线程要做的事情
  11. 可以在这里执行批量插入等耗时的事情
  12. 2018-07-16 22:15:47.816 INFO 10516 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  13. 2018-07-16 22:15:48.833 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  14. 异步线程要做的事情
  15. 可以在这里执行批量插入等耗时的事情
  16. 2018-07-16 22:15:48.834 INFO 10516 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  17. 2018-07-16 22:15:48.986 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  18. 异步线程要做的事情
  19. 可以在这里执行批量插入等耗时的事情
  20. 2018-07-16 22:15:48.987 INFO 10516 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

通过以上日志可以发现,[async-service-]是有多个线程的,显然已经在配置的线程池中执行了,并且每次请求中,Controller的起始和结束日志都是连续打印的,表明每次请求都快速响应了,而耗时的操作都留给线程池中的线程去异步执行;
虽然已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  4. import org.springframework.util.concurrent.ListenableFuture;
  5. import java.util.concurrent.Callable;
  6. import java.util.concurrent.Future;
  7. import java.util.concurrent.ThreadPoolExecutor;
  8. public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
  9. private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
  10. private void showThreadPoolInfo(String prefix) {
  11. ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
  12. if (null == threadPoolExecutor) {
  13. return;
  14. }
  15. logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
  16. this.getThreadNamePrefix(),
  17. prefix,
  18. threadPoolExecutor.getTaskCount(),
  19. threadPoolExecutor.getCompletedTaskCount(),
  20. threadPoolExecutor.getActiveCount(),
  21. threadPoolExecutor.getQueue().size());
  22. }
  23. @Override
  24. public void execute(Runnable task) {
  25. showThreadPoolInfo("1. do execute");
  26. super.execute(task);
  27. }
  28. @Override
  29. public void execute(Runnable task, long startTimeout) {
  30. showThreadPoolInfo("2. do execute");
  31. super.execute(task, startTimeout);
  32. }
  33. @Override
  34. public Future<?> submit(Runnable task) {
  35. showThreadPoolInfo("1. do submit");
  36. return super.submit(task);
  37. }
  38. @Override
  39. public <T> Future<T> submit(Callable<T> task) {
  40. showThreadPoolInfo("2. do submit");
  41. return super.submit(task);
  42. }
  43. @Override
  44. public ListenableFuture<?> submitListenable(Runnable task) {
  45. showThreadPoolInfo("1. do submitListenable");
  46. return super.submitListenable(task);
  47. }
  48. @Override
  49. public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
  50. showThreadPoolInfo("2. do submitListenable");
  51. return super.submitListenable(task);
  52. }
  53. }

如上所示,showThreadPoolInfo方法中将任务总数、已完成数、活跃线程数,队列大小都打印出来了,然后Override了父类的executesubmit等方法,在里面调用showThreadPoolInfo方法,这样每次有任务被提交到线程池的时候,都会将当前线程池的基本情况打印到日志中;
修改ExecutorConfig.java的asyncServiceExecutor方法,将ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改为ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor()

  1. @Bean(name = "asyncServiceExecutor")
  2. public Executor asyncServiceExecutor() {
  3. logger.info("start asyncServiceExecutor");
  4. //在这里修改
  5. ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
  6. //配置核心线程数
  7. executor.setCorePoolSize(corePoolSize);
  8. //配置最大线程数
  9. executor.setMaxPoolSize(maxPoolSize);
  10. //配置队列大小
  11. executor.setQueueCapacity(queueCapacity);
  12. //配置线程池中的线程的名称前缀
  13. executor.setThreadNamePrefix(namePrefix);
  14. // rejection-policy:当pool已经达到max size的时候,如何处理新任务
  15. // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
  16. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  17. //执行初始化
  18. executor.initialize();
  19. return executor;
  20. }

再次启动该工程测试

  1. 2018-07-16 22:23:30.951 INFO 14088 --- [nio-8087-exec-2] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [0], completedTaskCount [0], activeCount [0], queueSize [0]
  2. 2018-07-16 22:23:30.952 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  3. 异步线程要做的事情
  4. 可以在这里执行批量插入等耗时的事情
  5. 2018-07-16 22:23:30.953 INFO 14088 --- [async-service-1] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  6. 2018-07-16 22:23:31.351 INFO 14088 --- [nio-8087-exec-3] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [1], completedTaskCount [1], activeCount [0], queueSize [0]
  7. 2018-07-16 22:23:31.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  8. 异步线程要做的事情
  9. 可以在这里执行批量插入等耗时的事情
  10. 2018-07-16 22:23:31.353 INFO 14088 --- [async-service-2] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  11. 2018-07-16 22:23:31.927 INFO 14088 --- [nio-8087-exec-5] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [2], completedTaskCount [2], activeCount [0], queueSize [0]
  12. 2018-07-16 22:23:31.929 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  13. 异步线程要做的事情
  14. 可以在这里执行批量插入等耗时的事情
  15. 2018-07-16 22:23:31.930 INFO 14088 --- [async-service-3] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync
  16. 2018-07-16 22:23:32.496 INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]
  17. 2018-07-16 22:23:32.498 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : start executeAsync
  18. 异步线程要做的事情
  19. 可以在这里执行批量插入等耗时的事情
  20. 2018-07-16 22:23:32.499 INFO 14088 --- [async-service-4] c.u.d.e.executor.impl.AsyncServiceImpl : end executeAsync

注意这一行日志:

  1. 2018-07-16 22:23:32.496 INFO 14088 --- [nio-8087-exec-7] u.d.e.e.i.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [3], completedTaskCount [3], activeCount [0], queueSize [0]

这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了3个任务,完成了3个,当前有0个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一目了然。