關於java中@Async異步調用詳細解析附代碼
前言
異步調用與同步調用
- 同步調用:順序執行,通過調用返回結果再次執行下一個調用
- 異步調用:通過調用,無需等待返回結果,執行下一個調用
1. @Async講解
其@Async的註解代碼如下:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Async { String value() default ""; }
註解可以使用在類型以及方法中
通過value定義其值,默認是空
一般這個註解需要配合@EnableAsync,起源碼如下
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AsyncConfigurationSelector.class}) public @interface EnableAsync { Class<? extends Annotation> annotation() default Annotation.class; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Integer.MAX_VALUE; }
主要通過該註解放置在啟動類中進行配置啟動
在啟動類中添加如下:
@SpringbootApplication @EnableAsync public class Application{ public static void main(String[] args){ SrpingApplication.run(Application.class, args); } }
2. 用法
2.1 同步調用
從調用到返回函數結果才能執行下一步,稱為同步調用
service層 代碼:
public class Service{ public void test01() throws InterruptedException{ Thread.sleep(5000); System.out.println("保存日志"); } }
控制層代碼模塊:
public class Controler{ @Autowired private Service service; @GetMapping("/test") public String getTest(){ try{ System.out.println("開始"); service.test01(); System.out.println("結束"); }catch(InterruptedException e){ e.prinStackTrace(); } } }
通過springboot的啟動類啟動之後
輸出如下:
開始
// 此為等待5秒鐘,終端不顯示也不關閉
結束
2.2 異步調用
異步調用,執行函數不用等返回結果就可以執行下一步
service層 代碼:
主要是添加瞭@Async註解標識這個方法
public class Service{ @Async public void test01() throws InterruptedException{ Thread.sleep(500); System.out.println("保存日志"); } }
控制層代碼模塊:
通過調用service層函數
public class Controler{ @Autowired private Service service; @GetMapping("/test") public String getTest(){ try{ System.out.println("開始"); service.test01(); System.out.println("結束"); }catch(InterruptedException e){ e.prinStackTrace(); } } }
以及在啟動類中加入註解啟動 @EnableAsync
@SpringbootApplication @EnableAsync public class Application{ public static void main(String[] args){ SrpingApplication.run(Application.class, args); } }
3. 自定義線程池
對於線程池的一些基本知識可看我之前的文章:
java如何正確關閉線程以及線程池(代碼實踐含源碼分析)
java線程池的創建方式詳細分析(全)
如果不指定線程池,默認使用的線程池為SimpleAsyncTaskExecutor(來一個任務就創建一個線程,不斷創建線程導致CPU過高引發OOM),自帶的線程池一般都有弊端,一般推薦使用ThreadPoolExecutor(明確線程池的資源,規避風險)
具體如下:
- newFixedThreadPool:定死瞭線程數,任務隊列還是無界的,(最大線程數隻有隊列滿瞭,最大線程數才會創建),所以會造成OOM
- newCachedThreadPool:沒有設置最大線程數上限,創建大量的線程容易卡頓或者直接OOM
通過自定義線程池可以調整線程池的配置,更好的資源利用
@Async這個註解查找 AsyncConfigurer接口(實現類為AsyncConfigurerSupport,默認配置和方法都是空),所以可重寫接口指定線程池。
- 通過實現接口AsyncConfigurer
- 繼承AsyncConfigurerSupport
- 自定義TaskExecutor(替代內置任務執行器)
第三種方法:
在application.xml中定義線程池的一些變量
thread.core.size=16 thread.max.size=16 thread.queue.size=30 thread.prefix=xx-
自定義線程池如下
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; @Configuration public class ThreadPoolConfig { // 線程名稱前綴 @Value("${thread.prefix}") private String threadPrefix; // 核心線程數 @Value("${thread.core.size}") private int coreSize; // 最大線程數 @Value("${thread.max.size}") private int maxSize; // 隊列長度 @Value("${thread.queue.size}") private int queueSize; // 通過bean註解註入 @Bean("xx") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //設置線程池參數信息 taskExecutor.setCorePoolSize(coreSize); taskExecutor.setMaxPoolSize(maxSize); taskExecutor.setQueueCapacity(queueSize); taskExecutor.setThreadNamePrefix(threadPrefix); taskExecutor.setWaitForTasksToCompleteOnShutdown(true); taskExecutor.setAwaitTerminationSeconds(30); //修改拒絕策略為使用當前線程執行 taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //初始化線程池 taskExecutor.initialize(); return taskExecutor; } }
到此這篇關於關於java中@Async異步調用詳細解析附代碼的文章就介紹到這瞭,更多相關java @Async異步調用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Springboot 如何使用@Async整合線程池
- Java Spring註解之@Async的基本用法和示例
- springboot @Async 註解如何實現方法異步
- 基於springcloud異步線程池、高並發請求feign的解決方案
- Spring 異步接口返回結果的四種方式