springboot:巢狀使用非同步註解@Async還會非同步執行嗎

北漂程式設計師發表於2021-08-29

一、引言

在前邊的文章《[springboot:使用非同步註解@Async的那些坑》中介紹了使用@Async註解獲取任務執行結果的錯誤用法,今天來分享下另外一種常見的錯誤。

二、程式碼演示

下面是我的controller的程式碼,

package com.atssg.controller;

import com.atssg.service.MyAsyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/sync/")
@RestController
public class SyncController2 {
    @Autowired
    private MyAsyncService syncService;

    @GetMapping("/test2")
    public String test2() {
        return syncService.syncMethod("hello world");
    }
}

在controller中呼叫了service層的syncMethod方法,下面看該方法的定義,

package com.atssg.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@Slf4j
@Service
public class MyAsyncService {
    @Autowired
    private SyncService syncService;
    public String syncMethod(String str) {
        String result = null;
        try {
            log.info("start,{}",System.currentTimeMillis());
            //呼叫method4方法,該方法中會橋套呼叫另外一個非同步方法
            Future<String> futureResult = syncService.method4(str);
            result = futureResult.get();
            log.info("end:{}",System.currentTimeMillis());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return result;
    }
}

method4方法是一個非同步的方法,在該方法內部會呼叫另外一個非同步的方法,下面看該method4方法的定義,

/**
     * 呼叫另一個非同步方法
     *
     * @param str
     * @return
     * @throws InterruptedException
     */
    public Future<String> method4(String str) throws InterruptedException {

        //method4方法睡眠10s
        Thread.sleep(1000 * 10);
        //呼叫另外一個非同步方法
        method1(str);
        return new AsyncResult<>(str);
    }

下面看method1方法,

public Future<String> method1(String str) throws InterruptedException {
        Thread.sleep(1000 * 10);
        return new AsyncResult<>(str);
    }

該方法也是睡眠10s。另外這兩個方法均是非同步方法,有小夥伴會疑惑,怎麼沒有標註非同步註解@Async那,這是因為該註解可以用在方法上也可以用在類上,在前面的文章中說過,不知道小夥伴還記得嗎,不清楚的可以再看下給註解的定義哦。下面是我的類,大體看下,

image

小夥伴們看到了嗎,我是在類上標註了@Async的哦,這樣對於該類中所有的方法都是起作用的,即所有方法都是非同步的。

按照正常的邏輯來分析,method4和method1都是非同步方法,且兩個方法均睡眠10s,那麼非同步執行的結果應該是10s多點,但這裡是在method4中呼叫了method1,即巢狀呼叫,那麼結果會是什麼樣子那。看下執行結果,

image

看到這個執行結果小夥伴是不是很驚訝,執行時間大約是20s多點,不相信的小夥伴可以多執行幾次,結果發現都是20s多點,這是為什麼,不應該是10s多點,難道非同步執行失效了嗎

三、總結

在非同步方法中呼叫另外一個非同步方法會導致非同步執行失敗,就是上面的執行結果,所以不要在非同步方法中再呼叫非同步方法,達到非同步執行的目的。有小夥伴會問這是為什麼那,事先透露下這個要從非同步的原理說起,非同步的原理是通過代理實現的,更多內容歡迎關注下集更精彩。

推薦閱讀
springboot:使用非同步註解@Async獲取執行結果的坑
springboot:非同步呼叫@Async

image

相關文章