首頁 > Java > java教程 > 主體

java中@Async非同步呼叫的方法

PHPz
發布: 2023-05-05 19:10:04
轉載
1759 人瀏覽過

    前言

    非同步呼叫與同步呼叫

    • 同步呼叫:順序執行,透過呼叫傳回結果再次執行下一個調用

    • 異步調用:透過調用,無需等待返回結果,執行下一個調用

    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非同步呼叫的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    相關標籤:
    來源:yisu.com
    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
    熱門教學
    更多>
    最新下載
    更多>
    網站特效
    網站源碼
    網站素材
    前端模板
    關於我們 免責聲明 Sitemap
    PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!