【七日打卡】Spring中如何方便的使用Async异步方法

简介: 在Spring提供了@Async来实现方法的异步调用。 即当调用标有@Async标识的方法时,调用线程不会等待被调用方法执行完后再继续执行后续操作,而对被调用的方法启动一个独立线程来执行。这种异步执行的方式通常用于处理接口中不需要返回给用户的数据处理

在Spring提供了@Async来实现方法的异步调用。 即当调用标有@Async标识的方法时,调用线程不会等待被调用方法执行完后再继续执行后续操作,而对被调用的方法启动一个独立线程来执行。

这种异步执行的方式通常用于处理接口中不需要返回给用户的数据处理。比如:

当注册的时候,只需要将用户信息返回用户,而关于信息的保存操作可以使用异步执行。

在异步方法中都是开发自己去定义具体对业务逻辑代码块,然后交给Spring容器管理并启动新线程执行相应的方法。在使用异步方法的时候需要特别注意的是线程池的配置以及任务中异常的处理。下面对这个功能进行简单介绍。

简介

功能开启注解:@EnableAsync通过在Spring的配置类中添加这个注解来开启Spring的异步方法功能。

异步方法标识注解Async,其定义为:

// 用于type和method
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
    String value() default "";
}
复制代码

当此注解用于类的时候,表示此类中的所有方法都为异步方法。此注解中的value属性可用于指定执行此异步方法的线程池。线程池的具体确定方法下面具体分析。

Spring线程池的选择和自定义配置线程池

在项目中我们通常不会自己手动创建线程,而是通过统一的线程池来执行task或者异步方法,使用这种方式来避免多人团队中由于自定义线程导致的资源耗尽的问题。在自定义线程池之前首先要了解Spring在执行异步任务或者方法的时候是怎么选择线程池的。

@Async对于线程池的选择顺序

9c3af40549fa11139ff33d620f4bf75.png

Spring在执行async标识的异步方法的时候首先会在Spring的上下文中搜索类型为TaskExecutor或者名称为“taskExecutor”的bean,当可以找到的时候,就将任务提交到此线程池中执行。当不存在以上线程池的时候,Spring会手动创建一个SimpleAsyncTaskExecutor执行异步任务。

另外当@Async注解的时候指定了value值,Spring会使用指定的线程池执行。比如以下:

@Async(value = "asyncTaskThreadPool")这个时候Spring会去上下文中找名字为asyncTaskThreadPool的bean,并执行异步任务,如果找不到会抛出异常。

自定义线程池和异常处理

在了解了@Async对于线程池的选择后,我们需要自定义线程池。自定义Async线程池有三种方式。

方法一:重写@Async获取线程池的方法。

配置@Async方法的线程池需要继承AsyncConfigurerSupport类,或者实现AsyncConfigurer接口,并重写getAsyncExecutor方法,代码如下:

@Configuration
@EnableAsync
public class ThreadPoolBeanFactory extends AsyncConfigurerSupport{
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor();
        asyncTaskThreadPool.setCorePoolSize(100);
        asyncTaskThreadPool.setMaxPoolSize(200);
        asyncTaskThreadPool.setQueueCapacity(11);
        asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {
            private final AtomicLong index = new AtomicLong(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Async-override-task-pool-thread-" + index.getAndIncrement());
            }
        });
        asyncTaskThreadPool.initialize();
        return asyncTaskThreadPool;
    }
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        //返回值为void的异步方法不会传递异常到此,当方法中出现异常的时候只会打印日志,重写此方法来自定义异常处理方法
        return null;
    }
}
复制代码

这种定义的方法缺点是没有定义明确的线程池bean对象。

方法二:自定义相应类型的线程池bean。

第二种方法是基于Spring @Async对线程池选择的原理来实现的,定义一个类型为TaskExecutor的bean,定义方式如下:

@Bean
public TaskExecutor asyncTaskThreadPool() {
    ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor();
    asyncTaskThreadPool.setCorePoolSize(100);
    asyncTaskThreadPool.setMaxPoolSize(200);
    asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {
        private final AtomicLong index = new AtomicLong(1);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "Async-task-pool-thread-" + index.getAndIncrement());
        }
    });
  // asyncTaskThreadPool.initialize();//当使用@Bean的时候不需要调用此方法,装载容器的时候会自动调用
    return asyncTaskThreadPool;
}
复制代码

测试

分别使用以上两种方式定义的线程池。代码如下:

@Async
public void test(){
    System.out.println(Thread.currentThread().getName());
}
复制代码

此时Spring会自动使用以上定义的线程池执行此方法。使用以上两种配置输出结果分别是:

Async-task-override-pool-thread-1
Async-task-pool-thread-1
复制代码

方法三 在Async注解中执行线程池名称

异步任务定义如下:

@Async(value = "asyncTaskThreadPool")
public void asyncTask2() {
    LOGGER.info("AsyncTask2 start.");
    LOGGER.info(Thread.currentThread().getName());
    LOGGER.info("AsyncTask2 finished.");
}
复制代码

此时Spring会在上下文中找名称为asyncTaskThreadPool的线程池来执行此任务。

@Async返回操作结果

异步任务可以通过定义返回类型为Future来实现返回值的接收,定义如下:

@Async
public Future<String> asyncTaskWithResult() {
    LOGGER.info("AsyncTaskWithResult start.");
    try {
        Thread.sleep(1000 * 10);
    } catch (Exception e) {
        return new AsyncResult<>("error" + e.getMessage());
    }
    LOGGER.info("AsyncTaskWithResult finished.");
    return new AsyncResult<>("success");
}
复制代码

单元测试代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AsyncApplicationTests {
    @Autowired
    private AsyncTaskService asyncTaskService;
    @Test
    public void asyncTest() throws Exception{
        Future<String> future = asyncTaskService.asyncTaskWithResult();
        while (!future.isDone()) {
            System.out.println("Wait asyncTaskWithResult.");
            Thread.sleep(1000);
        }
        System.out.println("asyncTaskWithResult result is:" + future.get());
        System.out.println("asyncTask finished.");
    }
}
复制代码

输出内容如下:

AsyncTaskWithResult start.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
AsyncTaskWithResult finished.
asyncTaskWithResult result is:success
asyncTask finished.


目录
相关文章
|
1月前
|
消息中间件 存储 Java
RabbitMQ 和 Spring Cloud Stream 实现异步通信
本文介绍了在微服务架构中,如何利用 RabbitMQ 作为消息代理,并结合 Spring Cloud Stream 实现高效的异步通信。内容涵盖异步通信的优势、RabbitMQ 的核心概念与特性、Spring Cloud Stream 的功能及其与 RabbitMQ 的集成方式。通过这种组合,开发者可以构建出具备高可用性、可扩展性和弹性的分布式系统,满足现代应用对快速响应和可靠消息传递的需求。
120 2
RabbitMQ 和 Spring Cloud Stream 实现异步通信
|
3月前
|
人工智能 安全 Java
Spring Boot 中使用 Function 和异步线程池处理列表拆分任务并汇总结果
在Java开发中,处理大规模数据时常常需要将列表拆分为多个子列表进行异步处理并汇总结果。本文介绍如何在Spring Boot中使用Function和异步线程池实现高效且可维护的代码,涵盖结果封装、线程池配置、列表拆分处理及结果汇总等关键步骤。
149 0
|
6月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
590 5
|
6月前
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
495 2
|
8月前
|
人工智能 自然语言处理 Java
Spring 集成 DeepSeek 的 3大方法(史上最全)
DeepSeek 的 API 接口和 OpenAI 是兼容的。我们可以自定义 http client,按照 OpenAI 的rest 接口格式,去访问 DeepSeek。自定义 Client 集成DeepSeek ,可以通过以下步骤实现。步骤 1:准备工作访问 DeepSeek 的开发者平台,注册并获取 API 密钥。DeepSeek 提供了与 OpenAI 兼容的 API 端点(例如),确保你已获取正确的 API 地址。
Spring 集成 DeepSeek 的 3大方法(史上最全)
|
10月前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
332 73
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
2129 121
|
12月前
|
存储 安全 Java
|
11月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
131 1
|
11月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
113 1