springboot:嵌套使用异步注解@Async还会异步执行吗

一、引言

在前边的文章《[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