springboot异步线程(二)

前言

上一篇中讲了关于TaskExecutor的一些相关知识,本篇就是实战篇,看看异步线程使如何使用的

正文

本篇文章使用springboot 2.2.1.RELEASE

一.前奏,直接使用,无任何配置

  1. 启动异步注解

在springboot启动类上添加注解@EnableAsync


@SpringBootApplication
@EnableAsync
public class AsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }
}

  1. 使用@Async注解

在需要的方法上使用@Async注解:


public interface AsyncService {
    @Async
    void testAsync();
    @Async
    void testOne();
}

@Service
public class AsyncServiceImpl implements AsyncService {
    private static final Logger log= LoggerFactory.getLogger(AsyncServiceImpl.class);
    @Override
    public void testAsync(){
        log.info("========testAsync运行=========="+Thread.currentThread().getName());
    }
    @Override
    public void testOne(){
        log.info("ThreadName:===one===="+Thread.currentThread().getName());
    }
}
  1. controller 调用
    在controller层调用异步方法,注意,异步方法最好通过注入的方式调用,如果是同类方法或工具类方法,@Async可能不会取作用。

@RestController
@RequestMapping("/test")
public class TestController {
    private final AsyncService asyncService;
    @Autowired
    public TestController(AsyncService asyncService) {
        this.asyncService = asyncService;
    }

    @GetMapping
    public String test(){
        asyncService.testAsync();
        asyncService.testOne();
        return "SUCCESS";
    }
}

  1. 结果

2019-12-27 15:06:30.159  INFO 14756 --- [         task-1] c.e.async.service.impl.AsyncServiceImpl  : ========testAsync运行==========task-1
2019-12-27 15:06:30.160  INFO 14756 --- [         task-2] c.e.async.service.impl.AsyncServiceImpl  : ThreadName:===one====task-2

  1. 问题

很多人到这里就差不多完事了,我第一次用springboot的异步也是这样。其实这里还有问题:

  • 为什么线程名字是task-,而不是很多博客所说的SimpleAsyncTaskExecutor-
  • 为什么应用在启动时创建了一个名为:applicationTaskExecutorExecutor Service
  1. 原因

这是因为springboot版本的原因:

  • springboot2.1版本新加入了明为TaskExecutionAutoConfiguration的一个配置类,在没有实现Executor的情况是会自动注入一个nameapplicationTaskExecutorThreadPoolTaskExecutor,具体属性可以点进去看一看,这里就不细说了,关键源码如下:
    @Lazy
	@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
			AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
	@ConditionalOnMissingBean(Executor.class)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return builder.build();
	}

  • springboot2.1版本之前,没有这个TaskExecutionAutoConfiguration类,所以才会是网上很多博客所说的SimpleAsyncTaskExecutor,具体的源码在下一篇文章中讲

二. ThreadPoolTaskExecutor

如何使用ThreadPoolTaskExecutor。


@Configuration
public class AsyncConfig {
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor testTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("courses-schedule-");
        //最大线程数10:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(10);
        //核心线程数3:线程池创建时候初始化的线程数
        executor.setCorePoolSize(3);
        //缓冲队列0:用来缓冲执行任务的队列
        executor.setQueueCapacity(5);
        //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 当线程池已满,且等待队列也满了的时候,直接抛弃当前线程(不会抛出异常)
//        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//        executor.initialize();
        return executor;
    }
}

注意在使用@Async注解时在注解里面带上参数:@Async("threadPoolTaskExecutor")

三. ConcurrentTaskExecutor

如何使用ConcurrentTaskExecutor。


    @Bean(name = "concurrentTaskExecutor")
    public TaskExecutor concurrentTaskExecutor () {
        return new ConcurrentTaskExecutor(
                Executors.newFixedThreadPool(3));
    }
    

注意在使用@Async注解时在注解里面带上参数:@Async("concurrentTaskExecutor")

四 接口实现

AsyncConfig:这里需要实现AsyncConfigurer接口并重写里面得接口


@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("courses-schedule-");
        //最大线程数10:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(10);
        //核心线程数3:线程池创建时候初始化的线程数
        executor.setCorePoolSize(3);
        //缓冲队列0:用来缓冲执行任务的队列
        executor.setQueueCapacity(5);
        //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 当线程池已满,且等待队列也满了的时候,直接抛弃当前线程(不会抛出异常)
//        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//        executor.initialize();
        return executor;
    }
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncExceptionHandler();
    }
}

AsyncExceptionHandler:


public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
        System.out.println("Exception Cause - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }
}

这个直接使用,不用做其他操作

五 说明

  1. 上面列出的方式,可以根据自己的需求做出选择,最好选择一种
  2. 如果存在两种及以上,请在使用@Async时加上需要使用哪一个处理,如果没有指明使用哪一种处理,最后会默认使用SimpleAsyncTaskExecutor来处理异步任务。

最后

本篇文章主要是使用方法,如何去使用springboot的异步线程。

项目源码

参考:

  1. Spring Boot Async Task Executor
  2. Spring 官方文档
  3. 新手也能看懂的 SpringBoot 异步编程指南
  4. TaskExecutionAutoConfiguration
posted @ 2019-12-16 21:24  guoyuchuan  阅读(1303)  评论(0编辑  收藏  举报