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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM