前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring框架提供的异步执行能力

Spring框架提供的异步执行能力

作者头像
田维常
发布2019-07-20 17:51:24
9530
发布2019-07-20 17:51:24
举报

一、前言

Spring Framework分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。Spring还具有支持线程池或在应用程序服务器环境中委托给CommonJ的接口的实现。最终,在公共接口背后使用这些实现抽象出了Java SE 5,Java SE 6和Java EE环境之间的差异。本节我们着重讲解@Async如何实现异步处理。

二、 @Scheduled

@Scheduled注释可以与触发器元数据一起添加到方法中。例如,以固定延迟每5秒调用以下方法,这意味着将从每个前一次调用的完成时间开始测量该周期,例如:

代码语言:javascript
复制
@Scheduled(fixedDelay=5000)public void doSomething() {    // something that should execute periodically}

如果需要固定费率执行,只需更改注释中指定的属性名称即可。在每次调用的连续开始时间之间测量的每5秒执行以下操作:

代码语言:javascript
复制
@Scheduled(fixedRate=5000)public void doSomething() {    // something that should execute periodically}

如果简单的周期性调度不够表达,则可以提供cron表达式。例如,以下内容仅在工作日执行:

代码语言:javascript
复制
@Scheduled(cron="*/5 * * * * MON-FRI")public void doSomething() {    // something that should execute on weekdays only}

请注意,要调度的方法必须具有void返回,并且不得指望任何参数。如果该方法需要与Application Context中的其他对象进行交互,则通常会通过依赖注入提供这些对象。

需要注意的是该注解默认是不会解析的,需要加上@EnableScheduling 来启动。

三、 @Async

可以在方法上添加@Async注释,以便异步调用该方法。换句话说,调用者将在调用时立即返回,并且该方法的实际执行将发生在Spring TaskExecutor中。

代码语言:javascript
复制
    @Async    public void dosomthingAsync() {
        System.out.println("--dosomthingAsync begin---");        // 模拟异步处理        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("--dosomthingAsync end---");    }

如上代码在方法dosomthingAsync上添加了@Async的注解,所以当我们调用dosomthingAsync方法时候,该方法会马上返回。

与使用@Scheduled注解的方法不同,@Async可以有返回值,因为它们将在运行时由调用者以“正常”方式调用,而不是由容器管理的调度任务调用。例如,以下是@Async注解的合法应用程序:

代码语言:javascript
复制
    @Componentpublic class AsyncTask {...    @Async    public CompletableFuture<String> dosomthingAsyncFuture() {
        System.out.println("--dosomthingAsync begin---");        CompletableFuture<String> future = new CompletableFuture<String>();
        // 模拟异步处理        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        future.complete("ok");        System.out.println("--dosomthingAsync end---");
        return future;    }}

如上代码调用该方法后,该方法会马上返回一个CompletableFuture对象,如果你一直持有这个CompletableFuture对象,那么等dosomthingAsyncFuture内业务处理异步处理完毕后,就可以从dosomthingAsyncFuture的get()方法获取到执行结果。那么Spring框架是如何做到我们dosomthingAsyncFuture时候会马上返回一个CompletableFuture那?其实其对该类进行了代理,经过代理后的上面的方法类似于:

代码语言:javascript
复制
public class AsynTaskProxy {
    public AsyncTask getAsyncTask() {        return asyncTask;    }
    public void setAsyncTask(AsyncTask asyncTask) {        this.asyncTask = asyncTask;    }
    private AsyncTask asyncTask;
    private TaskExecutor executor = new SimpleAsyncTaskExecutor();
    public CompletableFuture<String> dosomthingAsyncFuture() {
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override            public String get() {                try {                    return asyncTask.dosomthingAsyncFuture().get();                } catch (Throwable e) {                    throw new CompletionException(e);                }            }        });    }}

Spring会对AsyncTask类使用类似的AsynTaskProxy进行代理,并且会把AsynTask的实例注入到AsynTaskProxy内部,当我们调用AsynTask的dosomthingAsyncFuture方法时候,实际调用的是AsynTaskProxy的dosomthingAsyncFuture方法,后者则使用 CompletableFuture.supplyAsync开启了一个异步任务(其马上返回一个 CompletableFuture对象),并且使用默认的SimpleAsyncTaskExecutor线程池做为异步处理线程,然后异步任务内在具体调用了 AsyncTask实例的dosomthingAsyncFuture方法,并且在返回的future上获取执行结果。

默认情况下,Spring将搜索关联的线程池定义:Spring上下文容器中的唯一的org.springframework.core.task.TaskExecutor类型的bean,如果不存在,则查找名为“taskExecutor”的java.util.concurrent.Executo的 bean。如果两者都不存在,则将使用org.springframework.core.task.SimpleAsyncTaskExecutor的一个实例来处理异步方法调用。

在SimpleAsyncTaskExecutor中对每个异步任务对应开启一个线程来进行处理,会造成线程频繁创建与销毁,没有进行线程复用,所以我们可以创建自己的线程池,比如下面:

代码语言:javascript
复制
@Bean    public TaskExecutor bizExecutor() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setMaxPoolSize(8);        executor.setCorePoolSize(8);        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        executor.setQueueCapacity(5);
        return executor;    }

则当bizExecutor通过@Bean注入到Spring上下文中后,异步处理就使用其中线程池进行处理。

需要注意的是该注解默认是不会被解析的,需要加上@EnableAsync来启动。

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-19,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 Java后端技术栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、 @Scheduled
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com