@Async注解的使用教程以及常见的失效场景

前言:

在最近的工作当中,接触到了@Async这个注解的使用,于是我通过学习网上对于这个注解的讲解进行了一个总结,主要是为了对这个注解能够加深印象以及基本的概念。

目录

一、简介

二、基本使用

(一)不带有返回值的异步任务

(二)带有返回值的异步任务 

三、自定义线程池

四、常见失效场景

(一)主启动类没有添加@EnableAsync注解

(二)A方法调用被@Async注解修饰的B方法

(三)需要通过@Autowired或@Resource进行注入,不可手动new

(四)被@Async注解修饰的方法必须不可以是static和private,必须为public

五、结语


一、简介

在Spring当中,被@Async注解标记的方法,称为异步方法,这些方法会在线程当中独立执行,开发人员无需等待它的完成,可以继续其他的业务操作;Spring容器在初始化Bean时,会先判断Bean中是否使用了@Async注解,创建切点,根据切点创建代理,在调用@Async注解标注的方法时,会调用代理,执行切点的Invoke方法,将方法的执行交给线程池进行异步执行。

二、基本使用

(一)不带有返回值的异步任务

1、创建UserController类

@RestController
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("/getAll")
    public void getUsers(){
        System.out.println("业务开始");
        userService.getUsers();
        System.out.println("业务结束");
    }
}

2、创建UserService类,模拟业务操作

@Service
public class UserService {

    @Async
    public void getUsers(){
        try {
            Thread.sleep(2000);
            System.out.println("查询到了所有的用户信息!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

3、在启动类当中加上@EnableAsync注解,开启异步任务

@SpringBootApplication
@EnableAsync
public class DemoApplication {

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

}

4、调用结果@Async注解的使用教程以及常见的失效场景_第1张图片

(二)带有返回值的异步任务 

1、创建UserController类

@RestController
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("/getAll")
    public String getUsers() throws ExecutionException, InterruptedException {
        System.out.println("业务开始");
        Future result = userService.getUsers();
        System.out.println("业务结束");
        return result.get();
    }

}

2、创建UserService类,模拟业务操作

@Service
public class UserService {

    @Async
    public Future getUsers(){
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return new AsyncResult<>("异步任务执行好了");
    }

}

3、在启动类当中加上@EnableAsync注解,开启异步任务

@SpringBootApplication
@EnableAsync
public class DemoApplication {

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

}

4、调用结果

@Async注解的使用教程以及常见的失效场景_第2张图片  

三、自定义线程池

1、编写ExecutorConfig自定义配置类

@Configuration
public class ExecutorConfig {

    @Bean("htt")
    public Executor taskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数5:线程池创建时候初始化的线程数
        executor.setCorePoolSize(5);
        //最大线程数10:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(10);
        //缓冲队列100:用来缓冲执行任务的队列
        executor.setQueueCapacity(100);
        //允许线程的空闲时间30秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(30);
        //线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;
        //如果执行程序已关闭,则会丢弃该任务
        executor.setThreadNamePrefix("htt-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

2、修改UserService类,在@Async注解旁边指定我们的Bean名称,从而可以使用我们自定义的线程池

@Service
public class UserService {

    @Async("htt")
    public void getUsers(){
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

3、运行结果

四、常见失效场景

(一)主启动类没有添加@EnableAsync注解

(二)A方法调用被@Async注解修饰的B方法

我们在Controller层当中调用test方法,场景如下

@RestController
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("/getAll")
    public void getUsers(){
        System.out.println("业务开始");
        test();
        System.out.println("业务结束");
    }

    @Async
    public void test(){
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

输出结果

原因:这是因为@Async注解是基础Spring的AOP动态代理模式实现的,test这个方法并没有走Spring的代理类,调用的getUsers方法是原生的并不是Spring所代理的对象,因为并没有经过Spring容器托管,从而造成了注解的失效。

(三)需要通过@Autowired或@Resource进行注入,不可手动new

在Controller层当中手动new一个Service对象,调用其中的异步方法

@RestController
public class UserController {

    private UserService userService = new UserService();

    @RequestMapping("/getAll")
    public void getUsers(){
        System.out.println("业务开始");
        userService.getUsers();
        System.out.println("业务结束");
    }

}

结果如下

(四)被@Async注解修饰的方法必须不可以是static和private,必须为public

五、结语

以上就是我目前对于@Async注解的一些理解和总结,如有遗漏,欢迎在评论区补充!

你可能感兴趣的:(SpringBoot,Spring,spring,java,后端,spring,boot,maven)