zoukankan      html  css  js  c++  java
  • 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
        }
    }
    不积跬步,无以至千里。不积小流,无以成江海!
  • 相关阅读:
    Java接口
    【精】搭建redis cluster集群,JedisCluster带密码访问【解决当中各种坑】!
    java操作redis集群配置[可配置密码]和工具类(比较好用)
    关于Jedis是否线程安全的测试
    RedisDesktopManager如何使用命令行?
    多线程按行读取文件【我】
    根据开始字符串和结束字符串截取文件生成另一个文件(日志处理)【我】
    Redis操作List工具类封装,Java Redis List命令封装
    缓存数据库-redis数据类型和操作(list)
    Redis需要多少内存预留-内存占用多少才安全
  • 原文地址:https://www.cnblogs.com/rouqinglangzi/p/10691941.html
Copyright © 2011-2022 走看看