<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot @Async:魔法和陷阱

          共 10772字,需瀏覽 22分鐘

           ·

          2023-11-09 00:14

          來(lái)源:https://medium.com/

          ?? 歡迎加入小哈的星球 ,你將獲得: 專(zhuān)屬的項(xiàng)目實(shí)戰(zhàn)/ Java 學(xué)習(xí)路線 / 一對(duì)一提問(wèn) / 學(xué)習(xí)打卡/贈(zèng)書(shū)福利


          目前, 正在星球內(nèi)部帶小伙伴做第一個(gè)項(xiàng)目:全棧前后端分離博客,手摸手,后端 + 前端全棧開(kāi)發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開(kāi)發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了125小節(jié),累計(jì)20w+字,講解圖:805張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有410+小伙伴加入(早鳥(niǎo)價(jià)超低)

          @Async注解就像是springboot項(xiàng)目中性能優(yōu)化的秘密武器。是的,我們也可以手動(dòng)創(chuàng)建自己的執(zhí)行器和線程池,但@Async使事情變得更簡(jiǎn)單、更神奇。

          @Async注釋 允許我們?cè)诤笈_(tái)運(yùn)行代碼,因此我們的主線程可以繼續(xù)運(yùn)行,而無(wú)需等待較慢的任務(wù)完成。但是,就像所有秘密武器一樣,明智地使用它并了解它的局限性非常重要。

          在這篇文章中,我們將深入探討@Async 的魔力以及在 Spring Boot 項(xiàng)目中使用它時(shí)應(yīng)該注意的問(wèn)題。讓我們開(kāi)始吧!

          首先讓我們學(xué)習(xí)如何在應(yīng)用程序中使用 @Async 的基礎(chǔ)知識(shí)。

          我們需要在 Spring Boot 應(yīng)用程序中啟用@Async 。為此,我們需要將@EnableAsync注釋添加到配置類(lèi)或主應(yīng)用程序文件中。這將為應(yīng)用程序中使用@Async注釋的所有方法啟用異步行為。

          @SpringBootApplication
          @EnableAsync
          public class BackendAsjApplication {
          }

          我們還需要?jiǎng)?chuàng)建一個(gè) Bean,指定使用 @Async 注釋的方法的配置。我們可以設(shè)置最大線程池大小、隊(duì)列大小等。不過(guò),添加這些配置時(shí)要小心。否則,我們可能很快就會(huì)耗盡內(nèi)存。我通常還會(huì)添加一個(gè)日志,以在隊(duì)列大小已滿并且沒(méi)有更多線程來(lái)接收新傳入任務(wù)時(shí)發(fā)出警告。

           @Bean
           public ThreadPoolTaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(2);
            executor.setMaxPoolSize(2);
            executor.setQueueCapacity(500);
            executor.setThreadNamePrefix("MyAsyncThread-");
            executor.setRejectedExecutionHandler((r, executor1) -> log.warn("Task rejected, thread pool is full and queue is also full"));
            executor.initialize();
            return executor;
           }

          現(xiàn)在,讓我們使用它。假設(shè)我們有一個(gè)服務(wù)類(lèi),其中包含我們想要異步的方法。我們將使用@Async注釋此方法。

          @Service
          public class EmailService {
              @Async
              public void sendEmail() {
             
              }
          }

          在代碼示例中,您會(huì)看到多次提到EmailService和PurchaseService。這些只是示例。我不想將所有內(nèi)容都命名為“MyService”。因此,將其命名為更有意義的名稱(chēng)。在電子商務(wù)應(yīng)用程序中,您當(dāng)然希望您的 EmailService 是異步的,這樣客戶請(qǐng)求就不會(huì)被阻止

          現(xiàn)在,當(dāng)我們調(diào)用此方法時(shí),它將立即返回,從而釋放調(diào)用線程(通常是主線程)以繼續(xù)執(zhí)行其他任務(wù)。該方法將繼續(xù)在后臺(tái)執(zhí)行,稍后將結(jié)果返回給調(diào)用線程。由于我們?cè)谶@里用 void 標(biāo)記了 @Async 方法,因此我們對(duì)它何時(shí)完成并不真正感興趣。

          非常簡(jiǎn)單而且非常強(qiáng)大,對(duì)吧?(當(dāng)然,我們可以做更多配置,但上面的代碼足以運(yùn)行完全異步的任務(wù))

          但是,在我們開(kāi)始使用 @Async 注釋所有方法之前,我們需要注意一些問(wèn)題。

          1@Async方法需要位于不同的類(lèi)中

          使用 @Async 注釋時(shí),請(qǐng)務(wù)必注意,我們不能從同一類(lèi)中調(diào)用 @Async 方法。這是因?yàn)檫@樣做會(huì)導(dǎo)致無(wú)限循環(huán)并導(dǎo)致應(yīng)用程序掛起。

          以下是不應(yīng)該做的事情的示例:

          @Service
          public class PurchaseService {

              public void purchase(){
                  sendEmail();
              }

              @Async
              public void sendEmail(){
                  // Asynchronous code
              }
          }

          相反,我們應(yīng)該為異步方法使用單獨(dú)的類(lèi)或服務(wù)。

          @Service
          public class EmailService {

              @Async
              public void sendEmail(){
                  // Asynchronous code
              }
          }

          @Service
          public class PurchaseService {

              public void purchase(){
                  emailService.sendEmail();
              }

              @Autowired
              private EmailService emailService;
          }

          現(xiàn)在您可能想知道,我可以從另一個(gè)異步方法中調(diào)用異步方法嗎?最簡(jiǎn)潔的答案是不。當(dāng)調(diào)用異步方法時(shí),它會(huì)在不同的線程中執(zhí)行,并且調(diào)用線程會(huì)繼續(xù)執(zhí)行下一個(gè)任務(wù)。如果調(diào)用線程本身是異步方法,則它無(wú)法等待被調(diào)用的異步方法完成后再繼續(xù),這可能會(huì)導(dǎo)致意外行為。

          2@Async 和 @Transcational 配合不佳

          @Transactional 注釋用于指示方法或類(lèi)應(yīng)該參與事務(wù)。它用于確保一組數(shù)據(jù)庫(kù)操作作為單個(gè)工作單元執(zhí)行,并且在發(fā)生任何故障時(shí)數(shù)據(jù)庫(kù)保持一致?tīng)顟B(tài)。

          當(dāng)一個(gè)方法被@Transactional注解時(shí),Spring會(huì)在該方法周?chē)鷦?chuàng)建一個(gè)代理,并且該方法內(nèi)的所有數(shù)據(jù)庫(kù)操作都在事務(wù)上下文中執(zhí)行。Spring 還負(fù)責(zé)在調(diào)用方法之前啟動(dòng)事務(wù),并在方法返回后提交事務(wù),或者在發(fā)生異常時(shí)回滾事務(wù)。

          但是,當(dāng)您使用 @Async 注釋使方法異步時(shí),該方法將在與主應(yīng)用程序線程不同的單獨(dú)線程中執(zhí)行。這意味著該方法不再在 Spring 啟動(dòng)的事務(wù)上下文中執(zhí)行。因此,@Async方法內(nèi)的數(shù)據(jù)庫(kù)操作不會(huì)參與事務(wù),并且在出現(xiàn)異常時(shí)數(shù)據(jù)庫(kù)可能會(huì)處于不一致的狀態(tài)。

          @Service
          public class EmailService {

              @Transactional
              public void transactionalMethod() {
                  //database operation 1
                  asyncMethod();
                  //database operation 2
              }

              @Async
              public void asyncMethod() {
                  //database operation 3
              }
          }

          在此示例中,數(shù)據(jù)庫(kù)操作 1 和數(shù)據(jù)庫(kù)操作 2 在 Spring 啟動(dòng)的事務(wù)上下文中執(zhí)行。但是,數(shù)據(jù)庫(kù)操作 3 是在單獨(dú)的線程中執(zhí)行的,并且不是事務(wù)的一部分。

          因此,如果在執(zhí)行數(shù)據(jù)庫(kù)操作3之前發(fā)生異常,則數(shù)據(jù)庫(kù)操作1和數(shù)據(jù)庫(kù)操作2將按預(yù)期回滾,但數(shù)據(jù)庫(kù)操作3不會(huì)回滾。這可能會(huì)使數(shù)據(jù)庫(kù)處于不一致的狀態(tài)。

          當(dāng)然,有很多方法可以解決這個(gè)問(wèn)題,即使用 TransactionTemplate 之類(lèi)的東西來(lái)管理事務(wù),但開(kāi)箱即用,如果從轉(zhuǎn)換方法調(diào)用異步方法,最終會(huì)出現(xiàn)問(wèn)題。

          3@Async 阻塞問(wèn)題

          假設(shè)這是我們的 @Async 線程池的配置:

          @Bean
           public ThreadPoolTaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(2);
            executor.setMaxPoolSize(2);
            executor.setQueueCapacity(500);
            executor.setThreadNamePrefix("MyAsyncThread-");
            executor.setRejectedExecutionHandler((r, executor1) -> log.warn("Task rejected, thread pool is full and queue is also full"));
            executor.initialize();
            return executor;
           }

          這意味著在任何特定時(shí)刻,我們最多將運(yùn)行 2 個(gè) @Async 任務(wù)。如果有更多任務(wù)進(jìn)來(lái),它們將排隊(duì),直到隊(duì)列大小達(dá)到 500。

          但現(xiàn)在假設(shè),我們的 @Async 任務(wù)之一執(zhí)行起來(lái)花費(fèi)了太多時(shí)間,或者只是由于外部依賴(lài)而被阻止。這意味著所有其他任務(wù)將排隊(duì)并且執(zhí)行速度不夠快。根據(jù)您的應(yīng)用程序類(lèi)型,這可能會(huì)導(dǎo)致延遲。

          解決此問(wèn)題的一種方法是為長(zhǎng)時(shí)間運(yùn)行的任務(wù)使用單獨(dú)的線程池,為更緊急且不需要大量處理時(shí)間的任務(wù)使用單獨(dú)的線程池。我們可以這樣做:

          @Primary
           @Bean(name = "taskExecutorDefault")
           public ThreadPoolTaskExecutor taskExecutorDefault() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(2);
            executor.setMaxPoolSize(2);
            executor.setQueueCapacity(500);
            executor.setThreadNamePrefix("Async-1-");
            executor.initialize();
            return executor;
           }

           @Bean(name = "taskExecutorForHeavyTasks")
           public ThreadPoolTaskExecutor taskExecutorRegistration() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(2);
            executor.setMaxPoolSize(2);
            executor.setQueueCapacity(500);
            executor.setThreadNamePrefix("Async2-");
            executor.initialize();
            return executor;
           }

          然后要使用它,只需在 @Async 聲明中添加執(zhí)行器的名稱(chēng)即可:

          @Service
          public class EmailService {
              @Async("taskExecutorForHeavyTasks")
              public void sendEmailHeavy() {
                  //method implementation
              }
          }

          但是,請(qǐng)注意,我們不應(yīng)該在調(diào)用Thread.sleep()或的方法上使用@Async Object.wait(),因?yàn)樗鼤?huì)阻塞線程,并且使用@Async的目的將落空。

          4@Async 中的異常

          圖片

          另一件需要記住的事情是 @Async 方法不會(huì)向調(diào)用線程拋出異常。這意味著您需要在 @Async 方法中正確處理異常,否則它們將丟失。

          以下是不應(yīng)該做的事情的示例:

          @Service
          public class EmailService {

              @Async
              public void sendEmail() throws Exception{
                  throw new Exception("Oops, cannot send email!");
              }
          }

          @Service
          public class PurchaseService {
              
              @Autowired
              private EmailService emailService;

              public void purchase(){
                  try{
                      emailService.sendEmail();
                  }catch (Exception e){
                      System.out.println("Caught exception: " + e.getMessage());
                  }
              }
          }

          在上面的代碼中,異常在 asyncMethod() 中拋出,但不會(huì)被調(diào)用線程捕獲,并且 catch 塊不會(huì)被執(zhí)行。

          為了正確處理 @Async 方法中的異常,我們可以結(jié)合使用 Future 和 try-catch 塊。這是一個(gè)例子:

          @Service
          public class EmailService {

              @Async
              public Future<String> sendEmail() throws Exception{
                  throw new Exception("Oops, cannot send email!");
              }
          }

          @Service
          public class PurchaseService {

              @Autowired
              private EmailService emailService;

              public void purchase(){
                  try{
                      Future<String> future = emailService.sendEmail();
                      String result = future.get();
                      System.out.println("Result: " + result);
                  }catch (Exception e){
                      System.out.println("Caught exception: " + e.getMessage());
                  }
              }
          }

          通過(guò)返回 Future 對(duì)象并使用 try-catch 塊,我們可以正確處理和捕獲 @Async 方法中引發(fā)的異常。

          總之,Spring Boot中的@Async注釋是提高應(yīng)用程序性能和可伸縮性的強(qiáng)大工具。但是,小心使用它并注意它的局限性是很重要的。通過(guò)理解這些陷阱并使用CompletableFuture和Executor等技術(shù),您可以充分利用@Async注釋并將應(yīng)用程序提升到下一個(gè)級(jí)別。

          ?? 歡迎加入小哈的星球 ,你將獲得: 專(zhuān)屬的項(xiàng)目實(shí)戰(zhàn)/ Java 學(xué)習(xí)路線 / 一對(duì)一提問(wèn) / 學(xué)習(xí)打卡/贈(zèng)書(shū)福利


          目前, 正在星球內(nèi)部帶小伙伴做第一個(gè)項(xiàng)目:全棧前后端分離博客,手摸手,后端 + 前端全棧開(kāi)發(fā),從 0 到 1 講解每個(gè)功能點(diǎn)開(kāi)發(fā)步驟,1v1 答疑,直到項(xiàng)目上線。目前已更新了125小節(jié),累計(jì)20w+字,講解圖:805張,還在持續(xù)爆肝中.. 后續(xù)還會(huì)上新更多項(xiàng)目,目標(biāo)是將Java領(lǐng)域典型的項(xiàng)目都整一波,如秒殺系統(tǒng), 在線商城, IM即時(shí)通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),已有410+小伙伴加入(早鳥(niǎo)價(jià)超低)

              
              

                
                
                   
                   

          1. 我的私密學(xué)習(xí)小圈子~

          2. 妙用Java 8中的 Function接口,消滅if...else(非常新穎的寫(xiě)法)

          3. 搞懂異地多活,看這篇就夠了

          4. 中美程序員不完全對(duì)比,太真實(shí)了。。。

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下在看,加個(gè)星標(biāo),這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          點(diǎn)“在看”支持小哈呀,謝謝啦

          瀏覽 2170
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩乱国产 | 正在播放91大神 | 豆花国产在线综合 | 人人爱人人摸人人操 | 精品自拍视频 |