@Async注解

Spring3开始提供了@Async注解,该注解能够标注在办法或者类上,从而能够不便的实现办法的异步调用。调用者在调用异步办法时将立刻返回,办法的理论执行将提交给指定的线程池中的线程执行。

@Async注意事项:

  • @Async标注在类上时,示意该类的所有办法都是异步办法。
  • @Async注解的办法肯定要通过依赖注入调用(因为要通过代理对象调用),不能间接通过this对象调用,否则不失效。

@Async应用示例

1、Spring中启用@Async

创立一个配置类,并加上@EnableAsync注解即可

@Configuration@EnableAsyncpublic class SpringAsyncConfig{}

2、应用@Async

创立异步办法执行类

@Servicepublic class AsyncService {    // 无返回值的异步办法    @Async    public void noReturnMethod() {        String tName = Thread.currentThread().getName();        System.out.println("current thread name : " + tName);        System.out.println("noReturnMethod end");    }    // 有返回值的异步办法    @Async    public Future<String> withReturnMethod() {        String tName = Thread.currentThread().getName();        System.out.println("current thread name : " + tName);        return new AsyncResult<>("aaa");    }}

创立调用异步办法的类

@RestController@RequestMapping("/api/async/test/")public class AsyncController {    @Autowired    AsyncService asyncService;    // 无返回值    @GetMapping("/noReturn")    public String noReturn() {        asyncService.noReturnMethod();        return "success";    }    // 有返回值    @GetMapping("/withReturn")    public String withReturn() {        Future<String> future = asyncService.withReturnMethod();        try {            String res = future.get();// 阻塞获取返回值            System.out.println("res = " + res);        } catch (InterruptedException | ExecutionException e) {            e.printStackTrace();        }        return "success";    }}

Spring定义的线程池类

Spring 曾经定义的线程池类有如下一些:

  • SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,默认每次调用都会创立一个新的线程。
  • SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只实用于不须要多线程的中央。
  • ConcurrentTaskExecutor:Executor的适配类,不举荐应用。如果ThreadPoolTaskExecutor不满足要求时,才用思考应用这个类。
  • SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz应用,才须要应用此类。
  • ThreadPoolTaskExecutor :最常应用,举荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。

配置自定义线程池

异步办法默认的线程池

在@EnableAsync注解中有如下正文阐明:

By default, Spring will be searching for an associated thread pool definition:either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. Ifneither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}will be used to process async method invocations.

翻译一下就是:

Spring首先会通过上面两种形式查找作为异步办法的默认线程池:
1、查找惟一的一个TaskExecutor类型的bean
2、或者是一个名称为“taskExecutor”的Executor类型的Bean。
如果下面两种形式都没有查找到,则应用SimpleAsyncTaskExecutor作为异步办法的默认线程池

而SimpleAsyncTaskExecutor线程池去执行@Async标注的异步办法,因为该线程池不会重用线程,所以我的项目中举荐应用自定义的线程池。

配置异步办法默认自定义线程池

配置@Async默认的线程池有多种形式:

  1. 从新实现接口AsyncConfigurer
  2. 继承AsyncConfigurerSupport
  3. 自定义一个TaskExecutor类型的bean。
  4. 自定义一个名称为“taskExecutor”的Executor类型的Bean。

上面展现了通过形式1(从新实现接口AsyncConfigurer)来配置默认的自定义线程池:

// 配置类实现AsyncConfigurer接口的getAsyncExecutor()办法@Configuration@EnableAsyncpublic class SpringAsyncConfig implements AsyncConfigurer {    @Override    public Executor getAsyncExecutor() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(3);//外围池大小        executor.setMaxPoolSize(6);//最大线程数        executor.setKeepAliveSeconds(60);//线程闲暇工夫        executor.setQueueCapacity(10);//队列水平        executor.setThreadNamePrefix("my-executor1-");//线程前缀名称        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置回绝策略        executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程        executor.initialize();        return executor;    }}

不同异步办法配置不同线程池

有时候不同性能的异步办法须要配置不同的线程池,能够通过在@Async上指定线程池的名称来实现

@Configurationpublic class ExecutorConfig {    @Bean("customExecutor-1")// 自定义线程池1    public Executor customExecutor1() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(3);//外围池大小        executor.setMaxPoolSize(6);//最大线程数        executor.setKeepAliveSeconds(60);//线程闲暇工夫        executor.setQueueCapacity(10);//队列水平        executor.setThreadNamePrefix("customExecutor-1-");//线程前缀名称        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置回绝策略        executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程        executor.initialize();        return executor;    }    @Bean("customExecutor-2")// 自定义线程池2    public Executor customExecutor2() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(3);//外围池大小        executor.setMaxPoolSize(6);//最大线程数        executor.setKeepAliveSeconds(60);//线程闲暇工夫        executor.setQueueCapacity(10);//队列水平        executor.setThreadNamePrefix("customExecutor-2-");//线程前缀名称        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置回绝策略        executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程        executor.initialize();        return executor;    }}
@Async("customExecutor-1")public void method1(){}@Async("customExecutor-2")public void method2(){}

@Async的异样解决

当办法是带Future返回值的时候,Future.get()办法会抛出异样,所以异样捕捉是没问题的。然而当办法是不带返回值的时候,那么此时主线程就不能捕捉到异样,须要额定的配置来解决异样,能够有上面两种形式。

1、通过try-catch解决异样

间接在异步办法中应用try-catch来解决抛出的异样。这个办法也能够用于带Future返回值的异步办法。

2、通过实现AsyncUncaughtExceptionHandler接口

@Configuration@EnableAsyncpublic class SpringAsyncConfig implements AsyncConfigurer {    @Override    public Executor getAsyncExecutor() {        // 省略自定义线程池的代码    }    // 自定义异样解决    @Override    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {        return new AsyncUncaughtExceptionHandler() {            @Override            public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {                System.out.println(method.getName() + "产生异样!异样起因:" + throwable.getMessage() );            }        };    }}

参考资料

  • Spring应用@Async注解