欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot如何使用@Async實現(xiàn)異步任務(wù)

 更新時間:2023年09月27日 09:46:34   作者:唐宋xy  
這篇文章主要介紹了Springboot如何使用@Async實現(xiàn)異步任務(wù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

前言

在查詢大批量的數(shù)據(jù)的時候,如果需要查詢多個表中的數(shù)據(jù),或者不僅查詢數(shù)據(jù)庫,還需要取其他的系統(tǒng)中查詢數(shù)據(jù),然后將所有查詢到的數(shù)據(jù)一起返回,這個時候,如果是單線程查詢效率慢,這個時候多線程就可以解決這個查詢效率慢的問題,Springboot中提供了@Async注解,一鍵實現(xiàn)異步操作~

實戰(zhàn)

一、@Async配置

異步任務(wù)配置類

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
? ? /**
? ? ?* 異步任務(wù)線程池配置
? ? ?* @return
? ? ?*/
? ? @Override
? ? public Executor getAsyncExecutor() {
? ? ? ? ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
? ? ? ? executor.setCorePoolSize(10); // 設(shè)置線程初始化數(shù)量
? ? ? ? executor.setMaxPoolSize(100); // 設(shè)置線程池最大數(shù)量
? ? ? ? executor.setQueueCapacity(100); // 設(shè)置等待隊列的大小
? ? ? ? executor.setThreadNamePrefix("AsyncExecutorThread-"); // 設(shè)置線程名稱前綴
? ? ? ? executor.initialize(); //如果不初始化,導(dǎo)致找到不到執(zhí)行器
? ? ? ? return executor;
? ? }
? ? /**
? ? ?* 異步任務(wù)異常處理
? ? ?* 如果需要自定義對異步任務(wù)的異常進行處理,則自定義異常處理(實現(xiàn)AsyncUncaughtExceptionHandler接口),并在這個方法返回該對象
? ? ?* @return
? ? ?*/
? ? @Override
? ? public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
? ? ? ? return null;
? ? }
}

二、實現(xiàn)異步任務(wù)

在需要開啟異步的方法上面加上注解@Async表示調(diào)用該方法的時候,通過線程池獲取線程去執(zhí)行,如果在Class上加@Async則表示該類的所有方法都是異步執(zhí)行的方法

異步任務(wù)調(diào)用示例

Controller中調(diào)用service中的異步方法更新數(shù)據(jù),并直接返回

@Slf4j
@Controller
@RequestMapping("/api/dashboard")
public class DashboardController {
? @Autowired
? public DashboardService dashboardService;
? @RequestMapping(value = "/update", method = RequestMethod.PUT)
? @ResponseBody
? public Response update(DashboardLeadsDataVo vo) {
? ? dashboardService.update(vo);
? ? log.info("主線程執(zhí)行完畢,返回結(jié)果")
? ? return new Response();
? }
}

在Service中直接定義異步方法

@Slf4j
@Transactional
@Service
public class DashboardServiceImpl implements DashboardService {
? @Autowired
? private DashboardRepository dashboardRepository;
? /**
? ?* 異步執(zhí)行更新
? ?* @param vo
? ?*/
? @Async
? @Override
? public void update(DashboardLeadsDataVo vo) {
? ?log.info("線程:【{}】執(zhí)行更新【線索數(shù)據(jù)】異步任務(wù)【開始】", Thread.currentThread().getName());
? ? DashboardLeadsData dl = new DashboardLeadsData()
? ? BeanUtils.copyProperties(vo, dl);
?? ??? ?dashboardRepository.update(dl)
? ? log.info("線程:【{}】執(zhí)行更新【線索數(shù)據(jù)】異步任務(wù)【結(jié)束】", Thread.currentThread().getName());
? }
}

這樣可以通過日志看到,開啟了新的線程去執(zhí)行@Async注解的方法,主線程在調(diào)用完方法之后就直接可以返回了,子線程在異步執(zhí)行方法。

如果有大量的邏輯運算需要進行,那么就可以開啟多個子線程去異步執(zhí)行,然后主線程直接返回結(jié)果,響應(yīng)速度快。

三、等待所有子線程完成,主線程返回數(shù)據(jù)

上面的異步任務(wù)沒有問題,但是如果在查詢數(shù)據(jù)的時候,因為需要查詢多個數(shù)據(jù),并組裝之后返回頁面,所以可以開啟多個異步任務(wù)去執(zhí)行查詢,但是主線程在調(diào)用異步任務(wù)的方法之后,就直接返回了,子線程還沒有執(zhí)行完成,所以導(dǎo)致返回頁面的數(shù)據(jù)是沒有值的。

這個問題需要通過使用到異步任務(wù)的返回值:Future接口來實現(xiàn)返回值和主線程等待子線程執(zhí)行完畢返回

需求:

按照不同的條件查詢多個表中的數(shù)據(jù),并返回到頁面中展示

分析:

因為有多個表,并且查詢的數(shù)據(jù)量比較大,查詢時間肯定會比較長,那么如果是單線程執(zhí)行,頁面點擊查詢的時候,頁面白屏或者Loading的時間會比較長,所以需要開啟多線程異步去查詢,并在多個線程都查詢完數(shù)據(jù)之后主線程統(tǒng)一返回頁面渲染

實現(xiàn):

Controller中直接調(diào)用service中的方法
@Controller
@RequestMapping("/api/dashboard")
public class DashboardController {
? ? @Autowired
? ? public DashboardService dashboardService;
? ? /**
? ? ?* 根據(jù)時間查詢顯示的各種線索量
? ? ?* @return
? ? ?*/
? ? @RequestMapping(value = "/cardsData", method = RequestMethod.GET)
? ? @ResponseBody
? ? public Response<DashboardDataDto> getCardsData() {
? ? ? ? DashboardDataDto dto = dashboardService.getCardsDataByDate();
? ? ? ? return new Response<>(dto);
? ? }
}

由service中調(diào)用多個異步任務(wù)方法查詢數(shù)據(jù)并統(tǒng)一返回Controller

@Slf4j
@Transactional
@Service
public class DashboardServiceImpl implements DashboardService {
? ? @Autowired
? ? private DashboardRepository dashboardRepository;
?/**
? ? ?* 查詢各種線索量
? ? ?*/
? ? @Override
? ? public DashboardDataDto getCardsDataByDate() {
? ? ? ? List<Future> futureList = new ArrayList<>();
? ? ? ? DashboardDataDto dto = new DashboardDataDto();
? ? ? ? // 從Spring容器中獲取當(dāng)前類的對象,不能直接調(diào)用自身方法,否則@Async注解無效 或者在其他spring管理類中調(diào)用
? ? ? ? DashboardServiceImpl springProxyBean = SpringUtil.getBean(DashboardServiceImpl.class);
? ? ? ? // 獲取總數(shù)據(jù)
? ? ? ? Future<String> totalFuture = springProxyBean.getTotalCount(dto);
? ? ? ? futureList.add(totalFuture);
? ? ? ? // 獲取年份數(shù)據(jù)
? ? ? ? Future<String> yearFuture = springProxyBean.getTerritoryYearCount(dto);
? ? ? ? futureList.add(yearFuture);
? ? ? ?? ?// 判斷異步任務(wù)中的所有子線程是否執(zhí)行完成
? ? ? ? // Future接口不僅可以用來判斷線程的狀態(tài),還可以獲取到異步任務(wù)執(zhí)行的返回結(jié)果
? ? ? ? while (true) {
? ? ? ? ? ? if(futureList.size() > 0) {
? ? ? ? ? ? ? ? boolean isAllThreadDone = true;
? ? ? ? ? ? ? ? for (Future future : futureList) {
? ? ? ? ? ? ? ? ? ? // 判斷是否所有的線程都已經(jīng)執(zhí)行完成
? ? ? ? ? ? ? ? ? ? if(future == null || !future.isDone()) {
? ? ? ? ? ? ? ? ? ? ? ? isAllThreadDone = false;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ?/** 如果需要獲取異步任務(wù)的返回值,則先判斷是否執(zhí)行完成
? ? ? ? ? ? ? ? ? ? ?* if (future.isDone()) {
? ? ? ? ? ? ? ? ? ? ?* ? ?future.get();
? ? ? ? ? ? ? ? ? ? ?* ?}
? ? ? ? ? ? ? ? ? ? ?*/
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 都已經(jīng)執(zhí)行完成則break
? ? ? ? ? ? ? ? if(isAllThreadDone) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? log.info("主線程結(jié)束。。。。。。。");
? ? ? ? return dto;
? ? }?? ?
? ?? ?/**
? ? ?* 查詢總數(shù)據(jù)
? ? ?* @param dto
? ? ?*/
? ? @Async
? ? public Future<String> getTotalCount(DashboardDataDto dto) {
? ? ? ? log.info("線程:【{}】執(zhí)行查詢【總數(shù)據(jù)】異步任務(wù)【開始】", Thread.currentThread().getName());
? ? ? ? // 查詢領(lǐng)界累計線索量
? ? ? ? List<Object> totalLeadsCount = dashboardRepository.findTotalLeadsCount();
? ? ? ? if(!CollectionUtils.isEmpty(totalLeadsCount)) {
? ? ? ? ? ? dto.setTotalLeadsCount(((Number) totalLeadsCount.get(0)).intValue());
? ? ? ? }
? ? ? ? log.info("線程:【{}】執(zhí)行查詢【總數(shù)據(jù)】異步任務(wù)【結(jié)束】", Thread.currentThread().getName());
? ? ? ?? ?// 只可以返回Future接口實現(xiàn)類,并且將需要返回的數(shù)據(jù)傳遞
? ? ? ? return new AsyncResult<>(Thread.currentThread().getName());
? ? }
? ? /**
? ? ?* 查詢年份領(lǐng)界線索數(shù)據(jù)量
? ? ?* @param dto
? ? ?*/
? ? @Async
? ? public Future<String> getYearCount(DashboardDataDto dto) {
? ? ? ? log.info("線程:【{}】執(zhí)行查詢【年份數(shù)據(jù)量】異步任務(wù)【開始】", Thread.currentThread().getName());
? ? ? ? String currentYearStart = DateUtils.yearStart(0);
? ? ? ? String nextYearStart = DateUtils.yearStart(1);
? ? ? ? // 查詢年份累計線索量
? ? ? ? List<Object> yearsCountList = dashboardRepository.findYearsCountByDate(currentYearStart, nextYearStart);
? ? ? ? if(!CollectionUtils.isEmpty(yearsCountList)) {
? ? ? ? ? ? dto.setYearsCount(((Number) yearsCountList.get(0)).intValue());
? ? ? ? }
? ? ? ? log.info("線程:【{}】執(zhí)行查詢【年份數(shù)據(jù)量】異步任務(wù)【結(jié)束】", Thread.currentThread().getName());
? ? ? ? return new AsyncResult<>(Thread.currentThread().getName());
? ? }
}

Service中用到的 SpringUtil

如果是自身調(diào)用自身的異步方法(如果是調(diào)用其他的spring管理類中異步方法,無須通過手動獲取該對象,直接調(diào)用即可),需要通過Spring的容器中獲取到當(dāng)前類的對象,否則自身調(diào)用方法無效,因為沒有走spring的代理,@Async注解無法生效

@Component
public class SpringUtil implements ApplicationContextAware {
? ? private static ApplicationContext applicationContext = null;
? ? public static ApplicationContext getApplicationContext() {
? ? ? ? return applicationContext;
? ? }
? ? /**
? ? ?* Spring容器啟動后,會把 applicationContext 給自動注入進來,然后我們把 applicationContext
? ? ?* ?賦值到靜態(tài)變量中,方便后續(xù)拿到容器對象
? ? ?* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
? ? ?*/
? ? @Override
? ? public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
? ? ? ? SpringUtil.applicationContext = applicationContext;
? ? }
? ? @SuppressWarnings("unchecked")
? ? public static <T> T getBean(String beanId) {
? ? ? ? return (T) applicationContext.getBean(beanId);
? ? }
? ? public static <T> T getBean(Class<T> requiredType) {
? ? ? ? return (T) applicationContext.getBean(requiredType);
? ? }
}

調(diào)用說明

經(jīng)測試,主線程的log,會在所有的需要等待的子線程執(zhí)行完畢之后,才會返回到Controller,返回到頁面中的數(shù)據(jù)就是所有線程已查詢出來的數(shù)據(jù)對象,響應(yīng)速度加快

TODO

實現(xiàn)多個線程同時開始執(zhí)行某個任務(wù)或者多個線程都同時處于等待狀態(tài)時開始執(zhí)行某個任務(wù),可以使用CountDownLatch、CyclicBarrier來實現(xiàn),可以用來實現(xiàn)等待所有的子線程返回數(shù)據(jù),主線程再返回功能,后續(xù)再說明~~

四、@ASync無效說明

沒有在@SpringBootApplication啟動類或者異步任務(wù)配置類當(dāng)中添加注解@EnableAsync注解。

異步方法使用注解@Async的返回值只能為void或者Future。

沒有走Spring的代理類。因為@Transactional和@Async注解的實現(xiàn)都是基于Spring的AOP,而AOP的實現(xiàn)是基于動態(tài)代理模式實現(xiàn)的。那么注解失效的原因就很明顯了,有可能因為調(diào)用方法的是對象本身而不是代理對象,因為沒有經(jīng)過Spring容器。

注解的方法必須是public方法,編譯時非public方法會報錯

如果需要從類的內(nèi)部調(diào)用,需要先獲取其代理類

ApplicationContext.getBean(Class);

調(diào)用的是靜態(tài)(static)方法,被@Async修飾的方法不能是static,否則不會生效

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • IDEA快速搭建Java開發(fā)環(huán)境的教程圖解

    IDEA快速搭建Java開發(fā)環(huán)境的教程圖解

    這篇文章主要介紹了IDEA如何快速搭建Java開發(fā)環(huán)境,本文通過圖文并茂的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-11-11
  • 新版idea如何開啟多臺JVM虛擬機的流程步驟

    新版idea如何開啟多臺JVM虛擬機的流程步驟

    在IntelliJ?IDEA這個集成開發(fā)環(huán)境中(IDE),開啟JVM(Java?Virtual?Machine)通常是在運行Java應(yīng)用程序時的操作,本文給大家介紹了新版idea如何開啟多臺JVM虛擬機的流程步驟,需要的朋友可以參考下
    2024-10-10
  • IDEA 2021.2 激活教程及啟動報錯問題解決方法

    IDEA 2021.2 激活教程及啟動報錯問題解決方法

    這篇文章主要介紹了IDEA 2021.2 啟動報錯及激活教程,文章開頭給大家介紹了idea2021最新激活方法,關(guān)于idea2021啟動報錯的問題小編也給大家介紹的非常詳細,需要的朋友可以參考下
    2021-10-10
  • Mybatis中SQL的執(zhí)行過程詳解

    Mybatis中SQL的執(zhí)行過程詳解

    MyBatis框架通過映射文件或注解將Java代碼中的方法與數(shù)據(jù)庫操作進行映射,執(zhí)行過程包括SQL解析、參數(shù)綁定、SQL預(yù)編譯、執(zhí)行、結(jié)果映射、事務(wù)處理、緩存處理和日志記錄
    2024-12-12
  • SpringBoot結(jié)合Tess4J實現(xiàn)拍圖識字的示例代碼

    SpringBoot結(jié)合Tess4J實現(xiàn)拍圖識字的示例代碼

    圖片中的文字提取已經(jīng)越來越多地應(yīng)用于數(shù)據(jù)輸入和自動化處理過程,本文主要介紹了SpringBoot結(jié)合Tess4J實現(xiàn)拍圖識字的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2024-06-06
  • mybatis plus動態(tài)數(shù)據(jù)源切換及查詢過程淺析

    mybatis plus動態(tài)數(shù)據(jù)源切換及查詢過程淺析

    這篇文章主要介紹了mybatis plus動態(tài)數(shù)據(jù)源切換及查詢過程淺析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 淺談Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn)

    淺談Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn)

    這篇文章主要介紹了Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • SpringBoot中注冊Bean的方式總結(jié)

    SpringBoot中注冊Bean的方式總結(jié)

    這篇文章主要介紹了SpringBoot中注冊Bean的方式總結(jié),@ComponentScan + @Componet相關(guān)注解,@Bean,@Import和spring.factories這四種方式,文中代碼示例給大家介紹的非常詳細,需要的朋友可以參考下
    2024-04-04
  • Spring?Boot整合流控組件Sentinel的場景分析

    Spring?Boot整合流控組件Sentinel的場景分析

    Sentinel?提供簡單易用、完善的?SPI?擴展接口。您可以通過實現(xiàn)擴展接口來快速地定制邏輯,這篇文章主要介紹了Spring?Boot整合流控組件Sentinel的過程解析,需要的朋友可以參考下
    2021-12-12
  • springmvc Rest風(fēng)格介紹及實現(xiàn)代碼示例

    springmvc Rest風(fēng)格介紹及實現(xiàn)代碼示例

    這篇文章主要介紹了springmvc Rest風(fēng)格介紹及實現(xiàn)代碼示例,rest風(fēng)格簡潔,分享了HiddenHttpMethodFilter 的源碼,通過Spring4.0實現(xiàn)rest風(fēng)格源碼及簡單錯誤分析,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11

最新評論