Spring中异步执行方法(@Async)

官方文档:

@EnableAsync

The @Async annotation

官方案例:https://spring.io/guides/gs/async-method

一、在spring中使用异步处理 

1.@EnableAsync和@Async

首先,在配置类加上@EnableAsync来启用异步注解。

@EnableAsync//启用异步支持
@Configuration
public class AppConfig {
}

然后,使用@Async注解标记需要异步执行的方法。

@Async
void doSomething() {
    // this will be run asynchronously
}

@Async
void doSomething(String s) {
    // this will be run asynchronously
}

@Async
Future<String> returnSomething(int i) {
    // this will be run asynchronously
}

使用@Async标记的异步方法可以带参数,也可以带有返回值。返回值类型必须是Future或其子类,可以是下面几种类型:

  • java.util.concurrent.Future
  • org.springframework.util.concurrent.ListenableFuture(Spring提供),AsyncResult是其比较常见的实现。
  • java.util.concurrent.CompletableFuture(JDK8提供) 

需要说明的是,@Async默认会使用的SimpleAsyncTaskExecutor来执行,而该线程池不会复用线程。所以,通常要使用异步处理,我们都会自定义线程池。

2.Spring内置提供的线程池

Spring提供了许多TaskExecutor的内置实现。

  • SyncTaskExecutor:此实现不会异步运行调用。而是,每个调用都在调用线程中进行。它主要用于不需要多线程的情况下,例如在简单的测试案例中。
  • SimpleAsyncTaskExecutor:此实现不会复用线程,每次调用启动一个新线程。
  • ConcurrentTaskExecutor:此实现是java.util.concurrent.Executor实例的适配器。还有一个可选的替代者ThreadPoolTask​​Executor,将Executor配置参数公开为bean属性。很少需要直接使用ConcurrentTaskExecutor。但是,如果ThreadPoolTask​​Executor不够灵活,无法满足需求时可以选择ConcurrentTaskExecutor。
  • ThreadPoolTaskExecutor此实现是最常用的。它公开了用于配置java.util.concurrent.ThreadPoolExecutor的bean属性,并将其包装在TaskExecutor中。如果需要适应其他类型的java.util.concurrent.Executor,建议您改用ConcurrentTaskExecutor
  • WorkManagerTaskExecutor:此实现使用CommonJ WorkManager作为其支持服务提供者,并且是在Spring应用程序上下文中的WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。
  • DefaultManagedTaskExecutor:此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获得的ManagedExecutorService,为此替换了CommonJ WorkManager。

通常情况下,我们应该使用ThreadPoolTaskExecutor,其暴露了线程池的属性供我们进行自定义设置。当ThreadPoolTaskExecutor无法满足我们时,可以使用ConcurrentTaskExecutor

如果声明了多个线程池,则默认情况下,Spring会按照如下搜索顺序来使用线程池:

  • 上下文中的唯一TaskExecutor  bean。
  • 否则名为“ taskExecutor”的Executor  bean。
  • 如果上面两者都无法处理,则将使用SimpleAsyncTaskExecutor(默认实现)处理异步方法调用。

3.自定义线程池

可以通过实现AsyncConfigurer接口或者直接继承AsyncConfigurerSupport类来自定义线程池。

1.非完全托管Spring Bean

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        executor.initialize();//手动初始化
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncUncaughtExceptionHandler();
    }
}

在上面的示例中,ThreadPoolTask​​Executor不是完全托管的Spring bean

2.完全托管Spring Bean

如果要使用完全托管的Spring Bean,在getAsyncExecutor() 方法上添加@Bean注解。此时,不需要手动调用executor.initialize(),Bean在初始化之后会自动调用。

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        //executor.initialize();//不用手动调用
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncUncaughtExceptionHandler();
    }
}

4.异常处理

当@Async方法返回类型是Future时,我们可以借助Future.get()方法抛出的异常来进行异常处理,因为异常信息被包装进了Future。

try {
    Future<Integer> future = asynTask.doSomething();
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    //进行异常处理
    System.out.println("处理异常");
}

而当返回类型是void时,异常无法捕获和不能被传递,所以就需要使用AsyncUncaughtExceptionHandler了。

public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // handle exception
    }
}
不积跬步,无以至千里。不积小流,无以成江海!
原文地址:https://www.cnblogs.com/rouqinglangzi/p/10691941.html