SpringBoot系列:Spring Boot異步調(diào)用@Async

在實(shí)際開發(fā)中俱箱,有時候?yàn)榱思皶r處理請求和進(jìn)行響應(yīng)国瓮,我們可能會多任務(wù)同時執(zhí)行,或者先處理主任務(wù)匠楚,也就是異步調(diào)用巍膘,異步調(diào)用的實(shí)現(xiàn)有很多厂财,例如多線程芋簿、定時任務(wù)、消息隊(duì)列等璃饱,

這一章節(jié)与斤,我們就來講講@Async異步方法調(diào)用。

一荚恶、@Async使用演示

@Async是Spring內(nèi)置注解撩穿,用來處理異步任務(wù),在SpringBoot中同樣適用谒撼,且在SpringBoot項(xiàng)目中食寡,除了boot本身的starter外,不需要額外引入依賴廓潜。

而要使用@Async抵皱,需要在啟動類上加上@EnableAsync主動聲明來開啟異步方法善榛。

@EnableAsync
@SpringBootApplication
public class SpringbootApplication {

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

}

現(xiàn)假設(shè)有3個任務(wù)需要去處理,分別對應(yīng)AsyncTask類的taskOne呻畸、taskTwo移盆、taskThree方法,這里做了線程的sleep來模擬實(shí)際運(yùn)行伤为。

@Slf4j
@Component
public class AsyncTask {

    private Random random = new Random();
    
    public void taskOne() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)一執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }
    
    public void taskTwo() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)二執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }
    
    public void taskThree() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)三執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }

}

然后編寫測試類咒循,由于@Async注解需要再Spring容器啟動后才能生效,所以這里講測試類放到了SpringBoot的test包下绞愚,使用了SpringBootTest叙甸。

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootApplication.class)
public class AsyncTaskTest {

    @Autowired
    private AsyncTask asyncTask;

    @Test
    public void doAsyncTasks(){
        try {
            long start = System.currentTimeMillis();
            asyncTask.taskOne();
            asyncTask.taskTwo();
            asyncTask.taskThree();
            Thread.sleep(5000);
            long end = System.currentTimeMillis();
            log.info("主程序執(zhí)行完成耗時{}秒", (end - start)/1000f);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

運(yùn)行測試方法,可以在控制臺看到任務(wù)一二三按順序執(zhí)行位衩,最后主程序完成蚁署,這和我們的預(yù)期一樣,因?yàn)槲覀儧]有任何額外的處理蚂四,他們就是普通的方法光戈,按編碼順序依次執(zhí)行。

而如果要使任務(wù)并發(fā)執(zhí)行遂赠,我們只需要在任務(wù)方法上使用@Async注解即可久妆,需要注意的是@Async所修飾的方法不要定義為static類型,這樣異步調(diào)用不會生效跷睦。

@Slf4j
@Component
public class AsyncTask {

    private Random random = new Random();

    //@Async所修飾的函數(shù)不要定義為static類型筷弦,這樣異步調(diào)用不會生效
    @Async
    public void taskOne() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)一執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }

    @Async
    public void taskTwo() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)二執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }

    @Async
    public void taskThree() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)三執(zhí)行完成耗時{}秒", (end - start)/1000f);
    }

}

然后我們在運(yùn)行測試類,這個時候輸出可能就五花八門了抑诸,任意任務(wù)都可能先執(zhí)行完成烂琴,也有可能有的方法因?yàn)橹鞒绦蜿P(guān)閉而沒有輸出。

二蜕乡、Future獲取異步執(zhí)行結(jié)果

上面演示了@Async奸绷,但是有時候除了需要任務(wù)并發(fā)調(diào)度外,我們還需要獲取任務(wù)的返回值层玲,且在多任務(wù)都執(zhí)行完成后再結(jié)束主任務(wù)号醉,這個時候又該怎么處理呢?

在多線程里通過Callable和Future可以獲取返回值辛块,這里也是類似的畔派,我們使用Future返回方法的執(zhí)行結(jié)果,AsyncResult<T>是Future的一個實(shí)現(xiàn)類润绵。

@Slf4j
@Component
public class FutureTask {

    private Random random = new Random();

    //@Async所修飾的函數(shù)不要定義為static類型线椰,這樣異步調(diào)用不會生效
    @Async
    public Future<String> taskOne() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)一執(zhí)行完成耗時{}秒", (end - start)/1000f);
        return new AsyncResult <>("任務(wù)一Ok");
    }

    @Async
    public Future<String> taskTwo() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)二執(zhí)行完成耗時{}秒", (end - start)/1000f);
        return new AsyncResult <>("任務(wù)二OK");
    }

    @Async
    public Future<String> taskThree() throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        log.info("任務(wù)三執(zhí)行完成耗時{}秒", (end - start)/1000f);
        return new AsyncResult <>("任務(wù)三Ok");
    }

}

在AsyncResult中:

  • isDone()方法可以用于判斷異步方法是否執(zhí)行完成,若任務(wù)完成尘盼,則返回true
  • get()方法可用于獲取任務(wù)執(zhí)行后返回的結(jié)果
  • cancel(boolean mayInterruptIfRunning)可用于取消任務(wù)憨愉,參數(shù)mayInterruptIfRunning表示是否允許取消正在執(zhí)行卻沒有執(zhí)行完畢的任務(wù)呜魄,如果設(shè)置true,則表示可以取消正在執(zhí)行過程中的任務(wù)
  • isCancelled()方法表示任務(wù)是否被取消成功莱衩,如果在任務(wù)正常完成前被取消成功爵嗅,則返回 true
  • get(long timeout, TimeUnit unit)用來獲取執(zhí)行結(jié)果,如果在指定時間內(nèi)笨蚁,還沒獲取到結(jié)果睹晒,就直接返回null
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootApplication.class)
public class AsyncTaskTest {

    @Autowired
    private FutureTask futureTask;

    @Test
    public void doFutureTasks(){
        try {
            long start = System.currentTimeMillis();
            Future <String> future1 = futureTask.taskOne();
            Future <String> future2 = futureTask.taskTwo();
            Future <String> future3 = futureTask.taskThree();
            //3個任務(wù)執(zhí)行完成之后再執(zhí)行主程序
            do {
                Thread.sleep(100);
            } while (future1.isDone() && future2.isDone() && future3.isDone());
            log.info("獲取異步方法的返回值:{}", future1.get());
            Thread.sleep(5000);
            long end = System.currentTimeMillis();
            log.info("主程序執(zhí)行完成耗時{}秒", (end - start)/1000f);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

運(yùn)行測試類,我們可以看到任務(wù)一二三異步執(zhí)行了括细,主任務(wù)最后執(zhí)行完成伪很,而且可以獲取到任務(wù)的返回信息。

源碼地址:https://github.com/imyanger/springboot-project/tree/master/p23-springboot-async

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奋单,一起剝皮案震驚了整個濱河市锉试,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌览濒,老刑警劉巖呆盖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贷笛,居然都是意外死亡应又,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門乏苦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來株扛,“玉大人,你說我怎么就攤上這事汇荐《淳停” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵掀淘,是天一觀的道長旬蟋。 經(jīng)常有香客問我,道長繁疤,這世上最難降的妖魔是什么咖为? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任秕狰,我火速辦了婚禮稠腊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸣哀。我一直安慰自己架忌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布我衬。 她就那樣靜靜地躺著叹放,像睡著了一般饰恕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上井仰,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天埋嵌,我揣著相機(jī)與錄音,去河邊找鬼俱恶。 笑死雹嗦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的合是。 我是一名探鬼主播了罪,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼聪全!你這毒婦竟也來了泊藕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤难礼,失蹤者是張志新(化名)和其女友劉穎娃圆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛾茉,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踊餐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了臀稚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吝岭。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吧寺,靈堂內(nèi)的尸體忽然破棺而出窜管,到底是詐尸還是另有隱情,我是刑警寧澤稚机,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布幕帆,位于F島的核電站,受9級特大地震影響赖条,放射性物質(zhì)發(fā)生泄漏失乾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一纬乍、第九天 我趴在偏房一處隱蔽的房頂上張望碱茁。 院中可真熱鬧,春花似錦仿贬、人聲如沸纽竣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜓氨。三九已至聋袋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間穴吹,已是汗流浹背幽勒。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留港令,地道東北人代嗤。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像缠借,于是被迫代替她去往敵國和親干毅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容