关于多线程:Spring的Async注解实现异步方法

42次阅读

共计 4887 个字符,预计需要花费 13 分钟才能阅读完成。

@Async 注解

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

@Async 注意事项:

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

@Async 应用示例

1、Spring 中启用 @Async

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

@Configuration
@EnableAsync
public class SpringAsyncConfig{}

2、应用 @Async

创立异步办法执行类

@Service
public 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
@EnableAsync
public 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 上指定线程池的名称来实现

@Configuration
public 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
@EnableAsync
public 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 注解

正文完
 0