日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

springboot实现异步调用@Async

發布時間:2023/12/15 综合教程 23 生活家
生活随笔 收集整理的這篇文章主要介紹了 springboot实现异步调用@Async 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

介紹

所謂的異步執行其實就是使用多線程的方式實現異步調用


異步有什么好處呢?
如果一個業務邏輯執行完成需要多個步驟,也就是調用多個方法去執行,
這個時候異步執行比同步執行相應更快。不過要注意異步請求的順序和處理結果的順序最好一致,不然就達不到效果了

注意事項

1、在默認情況下,未設置TaskExecutor時,默認是使用SimpleAsyncTaskExecutor這個線程池,但此線程不是真正意義上的線程池,因為線程不重用,每次調用都會創建一個新的線程??赏ㄟ^控制臺日志輸出可以看出,每次輸出線程名都是遞增的。所以最好我們來自定義一個線程池。
2、調用的異步方法,不能為同一個類的方法(包括同一個類的內部類),簡單來說,因為Spring在啟動掃描時會為其創建一個代理類,而同類調用時,還是調用本身的代理類的,所以和平常調用是一樣的。其他的注解如@Cache等也是一樣的道理,說白了,就是Spring的代理機制造成的。所以在開發中,最好把異步服務單獨抽出一個類來管理。下面會重點講述

什么情況下會導致@Async異步方法會失效?

1.不要在本類中異步調用。即一個方法是異步方法,然后用另一個方法調用這個異步方法。
2.不要有返回值,使用void
3.不能使用本類的私有方法或者非接口化加注@Async,因為代理不到失效
4.異步方法不能使用static修飾
5.異步類需使用@Component注解,不然將導致spring無法掃描到異步類
6.SpringBoot框架必須在啟動類中增加@EnableAsync注解
7.異步方法不要和事物注解同時存在。可以在事物的方法中調用另外一個類中的異步方法。在調用Async方法的方法上標注@Transactional是管理調用方法的事務的,在Async方法上標注@Transactional是管理異步方法的事務,事務因線程隔離
8.諸如以上幾點的情況比如spring中的@Transactional還有cache注解也不能有以上幾點情況,否則也會失效的,因為本質都是因為代理的機制導致的

定義一個線程池

@Configuration
public class AsyncTaskPoolConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(200);
        executor.setQueueCapacity(50);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskExecutor-ws-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

PS:使用@Async 注解時,需要注意一下幾點:

當項目中只有一個線程池時,我們只需要寫 @Async 即可將需要異步的方法進行異步;
當項目中存在多個線程池,我們在寫異步時,需要注意如果只寫@Async注解沒有任何屬性則將此方法的執行異步到帶有 @Primary 注解修飾的線程池中執行。
還可以將方法異步到指定的線程池中,如 @Async(“threadPool”)則是將此方法異步到threadPool 線程池中執行。

package com.xsrt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors()*2);
        taskExecutor.setMaxPoolSize(60);
        taskExecutor.setQueueCapacity(20000);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.setThreadGroupName("Task-");
        taskExecutor.setThreadNamePrefix("Async-");
        taskExecutor.setBeanName("threadPoolTaskExecutor");
        return taskExecutor;
    }
}

如果代碼中需要多個線程池,可以按照如下方式配置

package com.xsrt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("threadPoolTaskExecutor")
    @Primary //指定當前線程池為主線程池
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors()*2);
        taskExecutor.setMaxPoolSize(60);
        taskExecutor.setQueueCapacity(2000);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.setThreadGroupName("Task-");
        taskExecutor.setThreadNamePrefix("Async-");
        taskExecutor.setBeanName("threadPoolTaskExecutor");
        return taskExecutor;
    }


    @Bean("threadPool")//其他線程池
    public ThreadPoolTaskExecutor threadPool(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors()*2);
        taskExecutor.setMaxPoolSize(60);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.setThreadGroupName("Task-");
        taskExecutor.setThreadNamePrefix("Pool-");
        taskExecutor.setBeanName("threadPoolTaskExecutor");
        return taskExecutor;
    }
}

異步方法調用如下

package com.ccbobe.websocket.async;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class AsyncService {

    @Async
    public String asyncPrimary(){
      log.info("執行 Primary 異步......");
      return null;
    }

    @Async("threadPool")
    public String asyncPools(){
        log.info("執行 threadPool 異步......");
        return null;
    }
}

使用@Async和@EnableAsync注解

首先使用@EnableAsync注解開啟異步調用功能,該注解可以放置的位置有:

啟動類上方

@EnableAsync
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
   }

調用異步的當前類上方

@EnableAsync
@RestController
public class TestAsyncController(){}

在配置類上方使用

@Configuration
@EnableAsync
public class AsyncTaskPoolConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(200);
        executor.setQueueCapacity(50);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskExecutor-ws-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

編寫異步請求

在異步執行的方法上添加注解:@Async

Component
@Log4j2
public class AsyncTask {

    //這里注入的是dubbo的服務,和異步請求沒有多大關系
    @Reference(check = false)
    private AuthorFacade authorFacade;

      /**
     * 獲取作者信息
     *
     * @param authorId 作者ID
     * @return 作者信息
     */
    @Async("taskExecutor")
    public Future<AuthorDTO> getAuthor(String authorId){
        try {
            System.out.println("執行線程的名字:"+Thread.currentThread().getName());
            return new AsyncResult<AuthorDTO>(authorFacade.getAuthor(authorId));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

在service里調用異步執行的方法

/**
* 異步調用獲取文章信息
*
* @param articleId 文章ID
* @return 文章信息
*/
@Override
public Map getArticleDetailAsync(String articleId){
//同步調用獲取文章信息
ArticleDTO articleDTO = articleFacade.getArticle(articleId);
//異步調用獲取作者信息
Future authorFuture = asyncTask.getAuthor(articleDTO.getAuthorId());

    Map<String,Object> map=new HashMap<>(10);
    map.put("article",articleDTO);
    try{
        map.put("author",authorFuture.get());
    }catch (Exception e){
        log.error(e.getMessage());
    }
    return map;
}

PS:1、當一個類去調用標注了異步注解的方法時,當前類其實就是主線程,而調用標注異步注解的方法其實就相當于一個子線程,只有當主線程運行完了,其實可以用通過一些例子來證實的,給異步執行的方法阻塞幾秒鐘,查看下線程的執行情況
2、實際上,@Async還有一個參數,通過Bean名稱來指定調用的線程池-比如上例中設置的線程池參數不滿足業務需求,可以另外定義合適的線程池,調用時指明使用這個線程池-缺省時springboot會優先使用名稱為'taskExecutor'的線程池,如果沒有找到,才會使用其他類型為TaskExecutor或其子類的線程池。

進階

有時候我們不止希望異步執行任務,還希望任務執行完成后會有一個返回值,在java中提供了Future泛型接口,用來接收任務執行結果,springboot也提供了此類支持,使用實現了ListenableFuture接口的類如AsyncResult來作為返回值的載體。比如上例中,我們希望返回一個類型為String類型的值,可以將返回值改造為:

    @Async
    public ListenableFuture<String> sayHello(String name) {
        String res = name + ":Hello World!";
        LoggerFactory.getLogger(Hello.class).info(res);
        return new AsyncResult<>(res);
    }

調用返回值:

    @Autowired
    private Hello hello;

    // 阻塞調用
    hello.sayHello("yan").get();
    // 限時調用
    hello.sayHello("yan").get(1, TimeUnit.SECONDS)

總結

以上是生活随笔為你收集整理的springboot实现异步调用@Async的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。