一、前言

Springboot中执行异步任务,可以使用线程池,也可以直接使用@Async注解来实现异步任务。

而@Async也不是可以随便使用的,如果使用方式不对,也可能做了无用功,@Async不生效,导致实际还是同步执行

二、原理

因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,没有经过Spring容器,无法使用代理对象调用

即被调用方法 和 调用处的代码都处在同一个类,所以只是相当于本类调用,并没有使用代理类 从而@Async并没有产生效果

二、场景

1、首先Application启动类必须设置@EnableAsync,否则不生效

@EnableAsync  //就是这个
@SpringBootApplication(scanBasePackages = {"com.msedu"})
public class DistributionApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DistributionApplication.class);
        application.setBannerMode(Banner.Mode.OFF);
        ConfigurableApplicationContext run = application.run(args);

    }

}

2、定义一个Controller,controller调用serviceA方法,serviceA方法调用serviceB方法(@Async)

(1)用例1

1、MyController
public class MyController {
    @PostMapping("/testSync")
    public RespMessage testSync() {
        aService.mainFunc();
        return RespHandler.success();
    }
}

2、AService
@Service
public class AService {
    @Override
	public void mainFunc() {
		logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		sync();
		logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
	}


    @Async
	public void sync() {
		logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		try {
			Thread.sleep(3000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
	}
}

//输出结果
2022-04-01 11:06:31.893 [TID: N/A] [http-nio-8103-exec-1] INFO  c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-1,start:1648782391893
2022-04-01 11:06:31.894 [TID: N/A] [http-nio-8103-exec-1] INFO  c.m.d.g.s.i.AService -异步方法-http-nio-8103-exec-1,start:1648782391894
2022-04-01 11:06:34.894 [TID: N/A] [http-nio-8103-exec-1] INFO  c.m.d.g.s.i.AService -异步方法-http-nio-8103-exec-1,end:1648782394894
2022-04-01 11:06:34.894 [TID: N/A] [http-nio-8103-exec-1] INFO  c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-1,end:1648782394894
2022-04-01 11:06:34.899 [TID: N/A] [http-nio-8103-exec-1] INFO  c.m.d.common.config.ControllerAspect -request(c9db041be0b4429ca432e2b8678f56b3)end, cost 3340ms


//结果
主方法mainFunc和子异步方法sync,同步执行了,【@Async不生效】


【
子异步方法sync虽然加了@Async,但由于sync是在同个类里面,对于主方法mainFunc来说,调用子异步方法sync是通过当前对象去调用的,而不是通过代理对象,因而@Async不生效
】

(2)用例2

1、MyController
public class MyController {
    @PostMapping("/testSync2")
    public RespMessage testSync2() {
        aService.mainFunc2();
        return RespHandler.success();
    }
}

2、AService
@Service
public class AService {

    @Autowire
    private BService bservice;

    @Override
	public void mainFunc2() {
		logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		bservice.sync();
		logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
	}
}

3、BService
@Service
public class BService {
    @Async
	public void sync() {
		logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		try {
			Thread.sleep(3000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
	}
    
}

//输出结果
2022-04-01 11:07:25.608 [TID: N/A] [http-nio-8103-exec-2] INFO  c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-2,start:1648782445608
2022-04-01 11:07:25.612 [TID: N/A] [http-nio-8103-exec-2] INFO  c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-2,end:1648782445612  //4ms


2022-04-01 11:07:25.621 [TID: N/A] [task-scheduler-1] INFO  c.m.d.g.s.i.BService -异步方法-task-scheduler-1,start:1648782445621   //真实异步
2022-04-01 11:07:28.621 [TID: N/A] [task-scheduler-1] INFO  c.m.d.g.s.i.BService -异步方法-task-scheduler-1,end:1648782448621


//结果
主方法Aservice.mainFunc和子异步方法Bservice.sync,异步执行了,【@Async生效】

(3)用例3

1、MyController
public class MyController {
    @PostMapping("/testSync3")
    public RespMessage testSync3() {
        aService.mainFunc3();
        return RespHandler.success();
    }
}

2、AService
@Service
public class AService {

    @Autowire
    private BService bservice;

    @Override
	public void mainFunc3() {
		logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		bservice.sync3();
		logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
	}
}

3、BService
@Service
public class BService {
    @Async
	public boolean sync3() {
		logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
		try {
			Thread.sleep(3000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
        return true;
	}
    
}

//输出结果
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean com.msedu.distribution.global.service.impl.PromoteConfigServiceImpl.syncReturnFlag()
	at org.springframework.aop.framework.CglibAopProxy.processReturnType(CglibAopProxy.java:391)
	at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:84)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:690)
	at com.msedu.distribution.global.service.impl.BService$$EnhancerBySpringCGLIB$$d60e57a8.syncReturnFlag(<generated>)
	at com.msedu.distribution.global.service.impl.AService.testSync3(TenantProdPromoteServiceImpl.java:331)


//结果
@Async的方法必须是void或者Future,否则报错

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐