SpringBoot - @Async异步任务与线程池

简介: SpringBoot - @Async异步任务与线程池

在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的。但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务。其实在Spring 3.x之后,就已经内置了@Async来完美解决这个问题。

两个注解:@EnableAysnc、@Aysnc

【1】TaskExecutor

Spring异步线程池的接口类,其实质是java.util.concurrent.Executor。

Spring 已经实现的异常线程池:

① SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。

② SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作,只适用于不需要多线程的地方。

③ ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 。

④ SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。

⑤ ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。


【2】@Async注解

源码如下:

/**
 * Annotation that marks a method as a candidate for <i>asynchronous</i> execution.
 * // 将方法标记为异步执行
 * Can also be used at the type level, in which case all of the type's methods are
 * considered as asynchronous.
 *// 也可以在类型级别使用,在这种情况下,所有类型的方法都被认为是异步的。
 * <p>In terms of target method signatures, any parameter types are supported.
 * // 目标方法中,可以支持任何类型的参数
 * However, the return type is constrained to either {@code void} or
 * {@link java.util.concurrent.Future}. 
 * // 然而,返回类型要么是void,要么是Future
 * In the latter case, you may declare the
 * more specific {@link org.springframework.util.concurrent.ListenableFuture} or
 * {@link java.util.concurrent.CompletableFuture} types which allow for richer
 * interaction with the asynchronous task and for immediate composition with
 * further processing steps.
 *
 * <p>A {@code Future} handle returned from the proxy will be an actual asynchronous
 * {@code Future} that can be used to track the result of the asynchronous method execution. 
 * // 代理返回的Future可以用来跟踪异步方法处理的结果
 * However, since the target method needs to implement the same signature,
 * it will have to return a temporary {@code Future} handle that just passes a value
 * through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link javax.ejb.AsyncResult},
 * or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}.
 *
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 3.0
 * @see AnnotationAsyncExecutionInterceptor
 * @see AsyncAnnotationAdvisor
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
    /**
     * A qualifier value for the specified asynchronous operation(s).
     * <p>May be used to determine the target executor to be used when executing this
     * method, matching the qualifier value (or the bean name) of a specific
     * {@link java.util.concurrent.Executor Executor} or
     * {@link org.springframework.core.task.TaskExecutor TaskExecutor}
     * bean definition.
     * <p>When specified on a class level {@code @Async} annotation, indicates that the
     * given executor should be used for all methods within the class. Method level use
     * of {@code Async#value} always overrides any value set at the class level.
     * @since 3.1.2
     */
    String value() default "";
}

【3】无返回值代码示例

Service如下:

@Service
public class AsyncService {
    @Async
    public void hello(){
        System.out.println("进入service。。。");
        try {
            Thread.sleep(3000);
            System.out.println("3S后数据开始处理中。。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Controller如下:

@RestController
public class AsyncController {
    @Autowired
    AsyncService asyncService;
    @GetMapping("/hello")
    public String hello(){
        System.out.println("进入Controller。。。");
        asyncService.hello();
        return "success";
    }
}

Service的方法上使用了@Async注解,如果使该注解起作用,则需要在主程序上添加@EnableAsync注解。

@SpringBootApplication
@EnableAsync
public class SpringBoot01CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBoot01CacheApplication.class, args);
    }
}

浏览器发起请求,控制台打印如下:

进入Controller。。。
2018-07-08 11:10:30.305  INFO 3996 --- [io-8080-exec-10] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
进入service。。。
3S后数据开始处理中。。

【4】使用Future获取异步方法返回值

Service如下:

 /**
     * 异步调用返回Future
     *
     * @param i
     * @return
     */
    @Async
    public Future<String> asyncInvokeReturnFuture(int i) {
        System.out.println("进入asyncInvokeReturnFuture...");
        Future<String> future;
        try {
            Thread.sleep(3000);
            System.out.println("3S后asyncInvokeReturnFuture数据开始处理中。。");
            future = new AsyncResult<String>("success:" + i);
        } catch (InterruptedException e) {
            future = new AsyncResult<String>("error");
        }
        return future;
    }

Controller修改如下:

    @GetMapping("/hello")
    public String hello(){
        System.out.println("进入Controller。。。");
        asyncService.hello();
        Future<String> future=asyncService.asyncInvokeReturnFuture(300);
        String s = null;
        try {
            s = future.get();
        } catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("异步方法返回值 : "+s);
        return s;
    }
}

控制台打印如下:

进入Controller。。。
2018-07-08 11:42:55.660  INFO 8152 --- [nio-8080-exec-2] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
进入service.hello()。。。
3S后hello数据开始处理中。。
进入asyncInvokeReturnFuture...
3S后asyncInvokeReturnFuture数据开始处理中。。
异步方法返回值 : success:300



【5】配置自定义线程池

源码示例如下:

@Configuration
public class MyExecutorConfig {
    /**
     * 自定义异步线程池
     * @return
     */
    @Bean
    public AsyncTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("Anno-Executor");
        executor.setMaxPoolSize(100);
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // .....
            }
        });
        // 使用预定义的异常处理类
        // executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

修改service再次测试如下:


说明使用了自定义异步线程池!



目录
相关文章
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
966 0
|
5月前
|
数据采集 存储 JSON
Python爬取知乎评论:多线程与异步爬虫的性能优化
Python爬取知乎评论:多线程与异步爬虫的性能优化
|
5月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
582 64
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
332 62
|
10月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
366 6
|
11月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
417 17
|
10月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
522 0
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
261 12
|
安全 调度 C#
STA模型、同步上下文和多线程、异步调度
【10月更文挑战第19天】本文介绍了 STA 模型、同步上下文和多线程、异步调度的概念及其优缺点。STA 模型适用于单线程环境,确保资源访问的顺序性;同步上下文和多线程提高了程序的并发性和响应性,但增加了复杂性;异步调度提升了程序的响应性和资源利用率,但也带来了编程复杂性和错误处理的挑战。选择合适的模型需根据具体应用场景和需求进行权衡。
373 0