Spring中异步方法的使用

Spring中异步方法的使用

1. 异步方法描述

异步方法,顾名思义就是调用后无须等待它的执行,而继续往下执行;@Async是Spring的一个注解,在Spring Boot中,我们只需要使用@Async注解就能简单的将原来的同步函数变为异步函数。

对于比较耗时的操作,我们可以抽取成异步方法来让主线程稳定快速继续执行,对于异步方法的执行结果可根据自己的要求是否需要在主线程处理;

2. 异步方法的实现步骤

在springboot应用我们可以使用简单的两个注解即可开启并使用异步方法;

  1. 创建一个普通的Service类,并有@Service修饰,表示这个服务类交给Spring管理;
  2. 在Service方法里定义一个普通的方法,使用@Async修饰;表示这是一个异步方法;
  3. 在引导类中添加@EnableAsync注解,使应用开启对异步方法的支持;

3. 实测一下

3.1 定义个Service类

@Slf4j
@Service
public class MyAsyncSevice {

    @Async
    public void myAsyncMehtod(){
        log.info("---> enter aysnc method");
        try {
            Thread.sleep(5000);
            int i = 1/0;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("---> end of async method");
    }
}

3.2 定义个测试Controlle

@Slf4j
@RestController
public class AsyncContorller {

    // 注入异步方法所在的类
    @Autowired
    MyAsyncSevice myAsyncSevice;

    @GetMapping("/async-test")
    public String asyncTest(){
        log.info("--> enter controller");
        myAsyncSevice.myAsyncMehtod();
        log.info("--> end of controller");
        return "hello";
    }
}

3.3 在启动类上启用异步

@EnableAsync
@SpringBootApplication
public class JsrDemoApplication {

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

}

3.4 测试

可以看到输出,controlle中调用了异步方法后继续执行,即使异步方法报错也不会controller的执行;

curl -X GET -H   "http://localhost:8080/async-test"
2021-08-20 17:42:50.810   --- [nio-8080-exec-1] AsyncContorller  : --> enter controller
2021-08-20 17:42:50.813   --- [nio-8080-exec-1] AsyncContorller  : --> end of controller
2021-08-20 17:42:50.821   --- [         task-1] MyAsyncSevice  : ---> enter aysnc method
2021-08-20 17:42:55.832   --- [         task-1] MyAsyncSevice  : ---> end of async method

4. 补充

4.1 Executor线程池

上面的测试,我们并没有创建新的线程和线程池,如果我们不配置线程池的Bean,Spring会自动创建SimpleAsyncTaskExecutor,并使用它来执行异步方法。定义线程池bean可参考如下:

@Bean
public Executor taskExecutor() {
 	ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 	executor.setCorePoolSize(3);
 	executor.setMaxPoolSize(3);
 	executor.setQueueCapacity(500);
 	executor.setThreadNamePrefix("MyThreadPool");
 	executor.initialize();
 	return executor;
}

4.2 异步返回Futrue

4.2.1 这样改造我们的异步方法,即返回一个Futrue类型的结果;

    @Async
    public Future myAsyncMehtod(){
        log.info("---> enter aysnc method");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("---> end of async method");

        return new AsyncResult("I am async method result");
    }

4.2.2 Contorller也改造下

使用了Futrue.get方法,controller主线程就会等待异步方法的执行结束,或等待超时后才会结束。

	@GetMapping("/async-test")
    public String asyncTest(){
        log.info("--> enter controller");
        Future ft = myAsyncSevice.myAsyncMehtod();
        try {
            ft.get(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        log.info("--> end of controller");
        return "hello";
    }

4.3 其他注意事项

  • 在同一个类中的方法调用,添加@async注解是失效的。原因是当你在同一个类中的时候,方法调用是在类中执行的,spring无法截获这个方法调用,也就不会在代理类里执行。
  • 可能会导致循环依赖,spring本身会解决循环依赖,但是因为@Async使用代理模式,spring在检查第二级缓存和原始对象是否相等时发现不相等,会抛出异常。
  • 无法获取请求上下文。
posted @ 2021-08-20 18:00  穷苦书生  阅读(5144)  评论(0编辑  收藏  举报