關於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!

推薦閱讀: