當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
SpringBoot - 优雅的实现【异步编程】
生活随笔
收集整理的這篇文章主要介紹了
SpringBoot - 优雅的实现【异步编程】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 概述
- V1.0 默認的實現
- Step1 搞配置類,開啟@EnableAsync
- Step2 搞方法標記 @Async注解
- Step3 搞調用
- Spring提供的線程池
- V2.0 實現@Async的自定義線程池
- V3.0 多個線程池處理
- 多個線程池
- 默認線程池
- 驗證一把
- 源碼
概述
Spring3開始提供了@Async注解,我們只需要在方法上標注此注解,此方法即可實現異步調用。 除此之外, 還得需要一個配置類,通過@EnableAsync 來開啟異步功能 。
V1.0 默認的實現
Step1 搞配置類,開啟@EnableAsync
我們需要使用@EnableAsync來開啟異步任務支持。
@EnableAsync注解可以直接放在SpringBoot啟動類上,也可以單獨放在其他配置類上。
我們這里選擇單獨搞個配置類
@Configuration @EnableAsync public class ThreadPoolTaskConfig {}Step2 搞方法標記 @Async注解
package com.artisan.jobs;import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:42* @mark: show me the code , change the world*/@Component @Slf4j public class AsyncJob {@Async public void job1() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job1 cost {} ms", endTime - beginTime);}@Async public void job2() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job2 cost {} ms", endTime - beginTime);} }Step3 搞調用
package com.artisan.controller;import com.artisan.jobs.AsyncJob; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:44* @mark: show me the code , change the world*/@RestController @RequestMapping("/async") @Slf4j public class AsyncController {@Autowiredprivate AsyncJob asyncJob;@RequestMapping("/job")public String task() throws InterruptedException {long beginTime = System.currentTimeMillis();// 執行異步任務asyncJob.job1();asyncJob.job2();// 模擬業務耗時Thread.sleep(1000);long cost = System.currentTimeMillis() - beginTime;log.info("main cost {} ms", cost);return "Task Cost " + cost + " ms";} }@Async注解在默認情況下用的是SimpleAsyncTaskExecutor線,不是真正意義上的線程池。
所以,線程名稱是 task-1 , task-2, task-3 , task-4…
可以看到,每次調用都會new一個線程。若系統中不斷的創建線程…
Spring提供的線程池
| SimpleAsyncTaskExecutor | 這個類沒有實現異步調用,只是一個同步操作。只適用于不需要多線程的地 |
| ConcurrentTaskExecutor | Executor的適配類,不推薦使用。如ThreadPoolTaskExecutor不滿足要求時,才用考慮使用這個類 |
| ThreadPoolTaskScheduler | 可以使用cron表達式 |
| ThreadPoolTaskExecutor | 推薦。 是對java.util.concurrent.ThreadPoolExecutor的包裝 |
V2.0 實現@Async的自定義線程池
package com.artisan.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;/*** @author 小工匠* @version 1.0* @description: 使用@EnableAsync來開啟異步任務支持,@EnableAsync注解可以直接放在SpringBoot啟動類上,也可以單獨放在其他配置類上。* 我們這里選擇使用單獨的配置類AsyncConfiguration。* @date 2022/3/1 0:41* @mark: show me the code , change the world*/@Configuration @EnableAsync public class ThreadPoolTaskConfig {/*** 核心線程數(默認線程數)*/private static final int CORE_POOL_SIZE = 5;/*** 最大線程數*/private static final int MAX_POOL_SIZE = 10;/*** 允許線程空閑時間(單位:默認為秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 緩沖隊列大小*/private static final int QUEUE_CAPACITY = 200;/*** 線程池名前綴*/private static final String THREAD_NAME_PREFIX = "Async-Service-";/*** 自定義線程池** @return*/@Bean("customAsyncPoolTaskExecutor")public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 當線程池的任務緩存隊列已滿并且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略* 通常有以下四種策略:* ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常。* ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。* ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)* ThreadPoolExecutor.CallerRunsPolicy:重試添加當前的任務,自動重復調用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}}其他保持不變, 重啟測試
V3.0 多個線程池處理
需求: 不同的業務,使用不同的線程池
多個線程池
package com.artisan.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;/*** @author 小工匠* @version 1.0* @description: 使用@EnableAsync來開啟異步任務支持,@EnableAsync注解可以直接放在SpringBoot啟動類上,也可以單獨放在其他配置類上。* 我們這里選擇使用單獨的配置類ThreadPoolTaskConfig* @date 2022/3/1 0:41* @mark: show me the code , change the world*/@Configuration @EnableAsync public class ThreadPoolTaskConfig {/*** 核心線程數(默認線程數)*/private static final int CORE_POOL_SIZE = 5;/*** 最大線程數*/private static final int MAX_POOL_SIZE = 10;/*** 允許線程空閑時間(單位:默認為秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 緩沖隊列大小*/private static final int QUEUE_CAPACITY = 200;/*** 線程池名前綴*/private static final String THREAD_NAME_PREFIX = "Biz1_Async-Service-";/*** 線程池名前綴*/private static final String THREAD_NAME_PREFIX_2= "Biz2_Async-Service-";/*** 自定義線程池** @return*/@Bean("tp1")public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 當線程池的任務緩存隊列已滿并且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略* 通常有以下四種策略:* ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常。* ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。* ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)* ThreadPoolExecutor.CallerRunsPolicy:重試添加當前的任務,自動重復調用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 自定義線程池** @return*/@Bean("tp2")public ThreadPoolTaskExecutor taskExecutor2() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX_2);/*** 當線程池的任務緩存隊列已滿并且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略* 通常有以下四種策略:* ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常。* ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。* ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)* ThreadPoolExecutor.CallerRunsPolicy:重試添加當前的任務,自動重復調用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}}配置 多個線程池, 然后 為@Async指定線程池名字即可實現 多個線程池處理
package com.artisan.jobs;import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:42* @mark: show me the code , change the world*/@Component @Slf4j public class AsyncJob {@Async("tp1")public void job1() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job1 cost {} ms", endTime - beginTime);}@Async("tp2")public void job2() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job2 cost {} ms", endTime - beginTime);}@Async()public void job3() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job3 cost {} ms", endTime - beginTime);} }默認線程池
@Async()沒標注,用哪個?????? 當系統存在多個線程池時,我們也可以配置一個默認線程池 ,配置類讓其實現AsyncConfigurer,并重寫getAsyncExecutor()方法,指定默認線程池
package com.artisan.multi;import lombok.extern.slf4j.Slf4j; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world* <p>* 實現AsyncConfigurer,并重寫getAsyncExecutor()方法,指定默認線程池*/@Configuration @EnableAsync @Slf4j public class DefaultAsyncConfiguration implements AsyncConfigurer {/*** 核心線程數(默認線程數)*/private static final int CORE_POOL_SIZE = 2;/*** 最大線程數*/private static final int MAX_POOL_SIZE = 10;/*** 允許線程空閑時間(單位:默認為秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 緩沖隊列大小*/private static final int QUEUE_CAPACITY = 200;/*** 線程池名前綴*/private static final String THREAD_NAME_PREFIX = "Default_Async-Service-";@Bean(name = "defaultPool")public ThreadPoolTaskExecutor executor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(CORE_POOL_SIZE);taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);taskExecutor.setQueueCapacity(KEEP_ALIVE_TIME);taskExecutor.setKeepAliveSeconds(QUEUE_CAPACITY);taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 當線程池的任務緩存隊列已滿并且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略* 通常有以下四種策略:* ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常。* ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。* ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)* ThreadPoolExecutor.CallerRunsPolicy:重試添加當前的任務,自動重復調用 execute() 方法,直到成功*/taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}/*** 指定默認線程池*/@Overridepublic Executor getAsyncExecutor() {return executor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> log.error("線程池執行任務發橫未知錯誤,執行方法:{}", method.getName(), ex);}}驗證一把
@RestController @RequestMapping("/async") @Slf4j public class AsyncController {@Autowiredprivate AsyncJob asyncJob;@RequestMapping("/job")public String task() throws InterruptedException {long beginTime = System.currentTimeMillis();// 執行異步任務asyncJob.job1();asyncJob.job2();asyncJob.job3();// 模擬業務耗時Thread.sleep(1000);long cost = System.currentTimeMillis() - beginTime;log.info("main cost {} ms", cost);return "Task Cost " + cost + " ms";} }完美匹配…
源碼
https://github.com/yangshangwei/boot2
總結
以上是生活随笔為你收集整理的SpringBoot - 优雅的实现【异步编程】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot - 优雅的实现【应
- 下一篇: SpringBoot - 实践阿里巴巴【