1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        千萬(wàn)不要這樣使用 @Async 注解 !

        共 5394字,需瀏覽 11分鐘

         ·

        2021-11-26 23:43

        點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)??

        來(lái)源:chuckfang.com/2019/11/13/Async

        在實(shí)際的項(xiàng)目中,對(duì)于一些用時(shí)比較長(zhǎng)的代碼片段或者函數(shù),我們可以采用異步的方式來(lái)執(zhí)行,這樣就不會(huì)影響整體的流程了。比如我在一個(gè)用戶請(qǐng)求中需要上傳一些文件,但是上傳文件的耗時(shí)會(huì)相對(duì)來(lái)說(shuō)比較長(zhǎng),這個(gè)時(shí)候如果上傳文件的成功與否不影響主流程的話,就可以把上傳文件的操作異步化,在spring boot中比較常見(jiàn)的方式就是把要異步執(zhí)行的代碼片段封裝成一個(gè)函數(shù),然后在函數(shù)頭使用@Async注解,就可以實(shí)現(xiàn)代碼的異步執(zhí)行(當(dāng)然首先得在啟動(dòng)類上加上@EnableAsync注解了)。

        圖片

        具體的使用方式這里我也就不再演示了,網(wǎng)上教大家使用@Async的很多。今天我要講的并不是怎么去使用@Async注解,而是講我在實(shí)際開(kāi)發(fā)過(guò)程中遇到的一個(gè)坑,希望你不要再犯。

        首先,再明確一點(diǎn),學(xué)習(xí)一個(gè)知識(shí),第一步是找到相應(yīng)的官網(wǎng)或是比較權(quán)威的網(wǎng)站。

        那么這個(gè)坑是什么呢?就是如果你在同一個(gè)類里面調(diào)用一個(gè)自己的被@Async修飾的函數(shù)時(shí),這個(gè)函數(shù)將不會(huì)被異步執(zhí)行,它依然是同步執(zhí)行的!所以你如果沒(méi)有經(jīng)過(guò)測(cè)試就想當(dāng)然的以為只要在方法頭加上@Async就能達(dá)到異步的效果,那么你很有可能會(huì)得到相反的效果。這個(gè)是很要命的。

        所以我來(lái)給你們演示一下,這個(gè)效果是多么恐怖。為什么說(shuō)它恐怖,是因?yàn)樵诔绦騿T的眼中,一切不符合期望的行為都是bug,bug能不恐怖嗎?

        首先我們先看一個(gè)正確使用的方式,建一個(gè)spring boot項(xiàng)目,如果你是用Intellij IDEA新建的項(xiàng)目,記得勾上web的依賴。

        項(xiàng)目建好后,我們?cè)趩?dòng)類上加上@EnableAsync注解:

        import?org.springframework.boot.SpringApplication;
        import?org.springframework.boot.autoconfigure.SpringBootApplication;
        import?org.springframework.scheduling.annotation.EnableAsync;

        @SpringBootApplication
        @EnableAsync
        public?class?AsyncdemoApplication?{

        ????public?static?void?main(String[]?args)?{
        ????????SpringApplication.run(AsyncdemoApplication.class,?args);
        ????}

        }

        然后再新建一個(gè)類Task,用來(lái)放三個(gè)異步任務(wù)doTaskOne、doTaskTwo、doTaskThree:

        import?org.springframework.scheduling.annotation.Async;
        import?org.springframework.stereotype.Component;

        import?java.util.Random;

        /**
        ?*?@author?https://www.chuckfang.top
        ?*?@date?Created?on?2019/11/12?11:34
        ?*/
        @Component
        public?class?Task?{

        ????public?static?Random?random?=?new?Random();

        ????@Async
        ????public?void?doTaskOne()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)一");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)一,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}

        ????@Async
        ????public?void?doTaskTwo()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)二");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)二,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}

        ????@Async
        ????public?void?doTaskThree()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)三");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)三,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}
        }

        在單元測(cè)試類上注入Task,在測(cè)試用例上測(cè)試這三個(gè)方法的執(zhí)行過(guò)程:

        @SpringBootTest
        class?AsyncdemoApplicationTests?{

        ????public?static?Random?random?=?new?Random();

        ????@Autowired
        ????Task?task;

        ????@Test
        ????void?contextLoads()?throws?Exception?{
        ????????task.doTaskOne();
        ????????task.doTaskTwo();
        ????????task.doTaskThree();
        ????????Thread.sleep(10000);
        ????}
        }

        為了讓這三個(gè)方法執(zhí)行完,我們需要再單元測(cè)試用例上的最后一行加上一個(gè)延時(shí),不然等函數(shù)退出了,異步任務(wù)還沒(méi)執(zhí)行完。

        我們啟動(dòng)看看效果:

        ?

        開(kāi)始做任務(wù)三 開(kāi)始做任務(wù)二 開(kāi)始做任務(wù)一 完成任務(wù)一,耗時(shí):4922毫秒 完成任務(wù)三,耗時(shí):6778毫秒 完成任務(wù)二,耗時(shí):6960毫秒

        ?

        我們看到三個(gè)任務(wù)確實(shí)是異步執(zhí)行的,那我們?cè)倏纯村e(cuò)誤的使用方法。

        我們?cè)跍y(cè)試類里面把這三個(gè)函數(shù)再寫一遍,并在測(cè)試用例上調(diào)用測(cè)試類自己的方法:

        @SpringBootTest
        class?AsyncdemoApplicationTests?{

        ????public?static?Random?random?=?new?Random();

        ????@Test
        ????void?contextLoads()?throws?Exception?{
        ????????doTaskOne();
        ????????doTaskTwo();
        ????????doTaskThree();
        ????????Thread.sleep(10000);
        ????}

        ????@Async
        ????public?void?doTaskOne()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)一");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)一,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}

        ????@Async
        ????public?void?doTaskTwo()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)二");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)二,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}

        ????@Async
        ????public?void?doTaskThree()?throws?Exception?{
        ????????System.out.println("開(kāi)始做任務(wù)三");
        ????????long?start?=?System.currentTimeMillis();
        ????????Thread.sleep(random.nextInt(10000));
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("完成任務(wù)三,耗時(shí):"?+?(end?-?start)?+?"毫秒");
        ????}
        }

        我們?cè)倏纯葱Ч?/p>

        ?

        開(kāi)始做任務(wù)一 完成任務(wù)一,耗時(shí):9284毫秒 開(kāi)始做任務(wù)二 完成任務(wù)二,耗時(shí):8783毫秒 開(kāi)始做任務(wù)三 完成任務(wù)三,耗時(shí):943毫秒

        ?

        它們竟然是順序執(zhí)行的!也就是同步執(zhí)行,并沒(méi)有達(dá)到異步的效果,這要是在生產(chǎn)上使用,豈不涼涼。

        這種問(wèn)題如果不進(jìn)行測(cè)試還是比較難發(fā)現(xiàn)的,特別是你想要異步執(zhí)行的代碼并不會(huì)執(zhí)行太久,也就是同步執(zhí)行你也察覺(jué)不出來(lái),或者說(shuō)你根本發(fā)現(xiàn)不了它是不是異步執(zhí)行。這種錯(cuò)誤也很容易犯,特別是當(dāng)你把一個(gè)類里面的方法提出來(lái)想要異步執(zhí)行的時(shí)候,你并不會(huì)想著新建一個(gè)類來(lái)放這個(gè)方法,而是會(huì)在當(dāng)前類上直接抽取為一個(gè)方法,然后在方法頭上加上@Async注解,你以為這樣就完事了,其實(shí)并沒(méi)有起到異步的作用!我也是在改進(jìn)我們項(xiàng)目的文件上傳時(shí)才發(fā)現(xiàn)這個(gè)問(wèn)題的。因?yàn)槲募蟼饕膊粫?huì)花費(fèi)太久,所以真的很隱蔽。

        其實(shí)@Async的這個(gè)性質(zhì)在官網(wǎng)上已經(jīng)有過(guò)說(shuō)明了,官網(wǎng):https://www.baeldung.com/spring-async是這樣說(shuō)的:

        ?

        First – let’s go over the rules – @Async has two limitations:

        • it must be applied to public methods only
        • self-invocation – calling the async method from within the same class – won’t work

        The reasons are simple – 「the method needs to be *public*」 so that it can be proxied. And 「self-invocation doesn’t work」 because it bypasses the proxy and calls the underlying method directly.

        ?

        文章在一開(kāi)始就提到了@Async的兩個(gè)限制,其中第二個(gè)就是調(diào)用自己類上的異步方法是不起作用的。下面也講了原因,就是這種使用方式繞過(guò)了代理而直接調(diào)用了方法,所以肯定是同步的了。從這里,我們也知道了另外一個(gè)知識(shí)點(diǎn),就是@Async注解其實(shí)是通過(guò)代理的方式來(lái)實(shí)現(xiàn)異步調(diào)用的。

        上面這個(gè)錯(cuò)誤使用方法,我目前沒(méi)有在網(wǎng)上看到過(guò)有人說(shuō)明。希望你看完我的博客之后不要再犯同樣的錯(cuò)誤了,或者你趕快檢查一下你自己的項(xiàng)目中有沒(méi)有這樣使用@Async注解的。如果覺(jué)得文章不錯(cuò),可以推薦給同事看哦,提醒他們正確使用@Async。

        1.?Java jar 如何防止被反編譯?代碼寫的太爛,害怕被人發(fā)現(xiàn)

        2.?動(dòng)圖圖解GC算法 - 讓垃圾回收動(dòng)起來(lái)!

        3.?一個(gè)基于 Spring Boot 2 + Redis + Vue 的商城管理系統(tǒng)

        4.?什么是QoS?

        最近面試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ù)奉上。

        文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

        謝謝支持喲 (*^__^*)

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一级偷拍 | 美女被草XX网站 | 成人无码免费 | 欧美色图1 | ass日本肉体艺术pics | 玖玖视频在线免费观看 | 国产精品揄拍一区二区 | 堆萌操逼网站 | aaa天堂 | av色在线 |