Spring Framework官方文档之Task Execution and Scheduling(任务执行与调度)

1.参看spring framework官方文档之任务执行与调度
2.参看spring framework api文档
3.请参见github代码学习任务调度
4.请参见github代码学习异步方法

一.简介

1.Spring框架通过TaskExecutor和TaskScheduler接口分别为任务的异步执行和调度(the asynchronous execution and scheduling of tasks)提供了抽象。Spring还提供了一些接口的实现,这些接口支持应用服务器环境中的线程池或委托给CommonJ。最终,公共接口背后的这些实现的使用抽象了Java SE 5、Java SE 6和Java EE环境之间的差异。

2.Spring还提供了集成类,以支持 Timer(自1.3以来一直是JDK的一部分)和 Quartz Scheduler 的调度。您可以通过使用带有对Timer或Trigger实例的可选引用的FactoryBean来设置这两个调度器。此外,Quartz Scheduler 和 Timer 都有一个方便的类,可以让您调用现有目标对象的方法(类似于普通的 MethodInvokingFactoryBean 操作)。

二.The Spring TaskExecutor Abstraction(Spring任务执行器抽象)

1.executor 是线程池概念的JDK名称。"executor"命名是因为无法保证底层实现实际上是一个池。An executor 可以是单线程的,甚至可以是同步的。Spring的抽象隐藏了Java SE和Java EE环境之间的实现细节。

2.Spring 的 TaskExecutor 接口与 java.util.concurrent.Executor 接口完全相同。事实上,最初,它存在的主要原因是在使用线程池时抽象出对Java 5的需求。该接口有一个方法(execute(Runnable task)),该方法根据线程池的语义和配置接受一个要执行的任务。

3.最初创建 TaskExecutor 是为了在需要的时候为其他Spring组件提供一个线程池抽象。ApplicationEventMulticaster、JMS的AbstractMessageListenerContainer和Quartz integration等组件都使用TaskExecutor abstraction来汇集线程(to pool threads)。然而,如果您的 bean 需要线程池行为,您也可以根据自己的需要使用这个抽象(TaskExecutor abstraction)。

2.1 TaskExecutor Types(TaskExecutor类型)

1.Spring包含许多预构建的TaskExecutor实现。在所有情况下,您都不应该需要实现自己的。Spring提供的变体如下:

  • SyncTaskExecutor:此实现不会异步运行调用。相反,每个调用都在调用线程中发生。它主要用于不需要多线程的情况,例如在简单的测试用例中。
  • SimpleAsyncTaskExecutor:此实现不重用任何线程。相反,它会为每次调用启动一个新线程。但是,它确实支持并发限制,该限制会阻塞任何超过该限制的调用,直到释放一个槽为止。如果您正在寻找真正的池,请参阅本列表后面的ThreadPoolTaskExecutor。
  • ConcurrentTaskExecutor:这个实现是java.util.concurrent.Executor实例的适配器。还有一种替代方法(ThreadPoolTaskExecutor),它将Executor配置参数公开为bean属性。很少需要直接使用ConcurrentTaskExecutor。但是,如果ThreadPoolTaskExecutor对您的需求不够灵活,那么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-obtained ManagedExecutorService,以替代CommonJ WorkManager。
2.2 Using a TaskExecutor(使用TaskExecutor)

1.Spring的TaskExecutor实现被用作简单的JavaBean。在下面的示例中,我们定义了一个bean,它使用ThreadPoolTaskExecutor异步打印一组消息:

public class TaskExecutorExample {
    private class MessagePrinterTask implements Runnable {
        private String message;
        public MessagePrinterTask(String message) {
            this.message = message;
        }
        public void run() {
            System.out.println(message);
        }
    }

    private TaskExecutor taskExecutor;
    public TaskExecutorExample(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }
    public void printMessages() {
        for(int i = 0; i < 25; i++) {
            taskExecutor.execute(new MessagePrinterTask("Message" + i));
        }
    }
}

在这里插入图片描述

2.3 ThreadPoolTaskExecutor与ThreadPoolExecutor?

别人的总结截图:
在这里插入图片描述

三.The Spring TaskScheduler Abstraction(Spring任务调度器抽象)

1.除了TaskExecutor抽象之外,Spring 3.0还引入了一个TaskScheduler,它提供了多种方法来调度任务,以便在将来的某个时候运行。最简单的方法是一个名为schedule的方法,它只接受一个Runnable和一个Date。这将导致任务在指定的时间之后运行一次。所有其他方法都能够调度任务以重复运行。固定速率和固定延迟方法用于简单的周期性执行,但是接受Trigger的方法要灵活得多。
在这里插入图片描述

3.1 Trigger接口

1.触发器 Trigger 的基本思想是,可以根据过去的执行结果甚至任意条件确定执行时间。如果这些决定确实考虑了前一个执行的结果,那么在TriggerContext中就可以获得该信息。TriggerContext 是最重要的部分。它封装了所有相关的数据,如果需要,将来可以对其进行扩展。TriggerContext是一个接口(默认情况下使用SimpleTriggerContext实现)。
在这里插入图片描述
在这里插入图片描述

3.1 Trigger实现

1.Spring提供了触发器接口的两种实现。最有趣的是CronTrigger它支持基于cron表达式调度任务

2.另一个实现是PeriodicTrigger,它接受一个固定的周期、一个可选的初始延迟值和一个布尔值,该布尔值指示该周期应被解释为固定速率还是固定延迟。由于TaskScheduler接口已经定义了以固定速率或固定延迟调度任务的方法,因此应尽可能直接使用这些方法。PeriodicTrigger实现的价值在于,您可以在依赖Trigger abstraction的组件中使用它。例如,允许周期性触发器、基于cron的触发器,甚至自定义触发器实现可能会很方便。这样的组件可以利用依赖注入,这样您就可以在外部配置这样的触发器,从而轻松地修改或扩展它们。

3.2 TaskScheduler实现

1.与Spring的TaskExecutor抽象一样,TaskScheduler安排的主要好处是将应用程序的调度需求与部署环境分离。当部署到不应该由应用程序本身直接创建线程的应用程序服务器环境时,这个抽象级别尤其相关。对于这种情况,Spring提供了一个TimerManagerTaskScheduler,该调度器委托给WebLogic或WebSphere上的CommonJ TimerManager,以及一个更近期的DefaultManagedTaskScheduler,该调度器委托给Java EE 7+环境中的JSR-236 ManagedScheduledExecutorService。两者通常都配置了JNDI查找。

2.当外部线程管理不是必需的时候,一个更简单的替代方案就是在应用程序中设置本地ScheduledExecutorService,可以通过Spring的ConcurrentTaskScheduler进行调整。为了方便起见,Spring还提供了一个ThreadPoolTaskScheduler,它在内部委托给ScheduledExecutorService,以提供与ThreadPoolTaskExecutor类似的公共bean风格的配置。这些变体对于宽松的应用服务器环境中的本地嵌入式线程池设置也非常适用 — 尤其是在Tomcat和Jetty上。

四.Annotation Support for Scheduling and Asynchronous Execution(对调度和异步执行的注释支持)

Spring为任务调度和异步方法执行提供了注释支持。

4.1 Enable Scheduling Annotations(启用调度注释)

1.要支持@Scheduled和@Async注释,可以将@EnableScheduling和@EnableAsync添加到@Configuration类中,如下例所示:

@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}

2.您可以为你的应用程序选择相关的注释。例如,如果只需要支持@Scheduled,你可以省略@EnableAsync。为了实现更细粒度的控制,还可以另外实现SchedulingConfigurer接口AsyncConfigurer接口或两者都实现。详细信息请参见SchedulingConfigurer和AsyncConfigurer javadoc。如果你喜欢XML配置,可以使用 task:annotation-driven 元素,如下例所示:注意,在下面的XML中,提供了一个executor引用来处理那些与@Async注释的方法相对应的任务,提供了scheduler引用来管理那些用@Scheduled注释的方法。
在这里插入图片描述

3.处理@Async注释的默认通知模式(default advice mode)是代理,它只允许通过代理拦截调用。同一类中的本地调用不能通过这种方式被拦截。对于更先进的拦截模式,考虑结合编译时间或加载时间编织切换到AspectJ模式。

4.2 @Scheduled注解

1.请注意,要调度的方法必须具有void返回,并且不能接受任何参数。如果该方法需要与应用程序上下文中的其他对象进行交互,那么这些对象通常是通过依赖项注入提供的

2.从Spring Framework 4.3开始,任何范围(scope)的bean都支持@Scheduled方法。确保您没有在运行时初始化同一个@Scheduled注释类的多个实例,除非您确实想要对每个这样的实例调度回调。与此相关的是,请确保不要在用@Scheduled注释并在容器中注册为常规Spring bean的bean类上使用@Configurable。否则,您将得到双重初始化(一次通过容器,一次通过@Configurable方面),结果是每个@Scheduled方法被调用两次。

4.3 @Async注解

1.可以在方法上提供@Async注释,以便异步调用该方法。换句话说,调用方在调用时立即返回,而方法的实际执行发生在已提交给Spring TaskExecutor的任务中

//在最简单的情况下,可以将注释应用于返回void的方法
@Async
void doSomething() {
    // this will be run asynchronously
}

//与用@Scheduled注释的方法不同,这些方法可以接受参数,因为它们是由调用者在运行时以“正常”的方式调用的,而不是由容器管理的调度任务。
@Async
void doSomething(String s) {
    // this will be run asynchronously
}

2.即使是返回值的方法也可以异步调用。但是,这些方法需要有一个 future 类型的返回值。这仍然提供了异步执行的好处,以便调用者可以在调用Future的get()之前执行其他任务

@Async
Future<String> returnSomething(int i) {
    // this will be run asynchronously
}

3.@Async方法不仅可以声明常规的java.util.concurrent. future返回类型,还可以声明Spring的org.springframework.util.concurrent.ListenableFuture,或者从Spring 4.2开始,JDK 8的 java.util.concurrent.CompletableFuture 返回类型,用于与异步任务进行更丰富的交互,以及与进一步处理步骤的即时组合

4.@Async不能与@PostConstruct等生命周期回调一起使用。要异步初始化Spring bean,当前必须使用单独的初始化Springbean,然后在目标上调用@Async注释的方法,如下例所示:
在这里插入图片描述

4.4 Executor Qualification with @Async

1.默认情况下,在方法上指定@Async时,使用的执行器是启用异步支持时配置的执行器(比如使用XML "annotation-driven"元素或AsyncConfigurer实现,如果有)。但是,当需要指示在执行给定方法时应使用默认值以外的执行器时,可以使用@Async注释的value属性。以下示例显示了如何执行此操作:在本例中,“otherExecutor”可以是Spring容器中任何Executor bean的名称,也可以是与任何Executor关联的限定符的名称(例如,使用qualifier元素或Spring的@qualifier注释指定)

@Async("otherExecutor")
void doSomething(String s) {
    // this will be run asynchronously by "otherExecutor"
}
4.5 Exception Management with @Async(使用@Async进行异常管理)

1.当@Async方法有一个 Future-typed 的返回值时,很容易管理在方法执行期间引发的异常,因为这个异常是在对 Future 结果调用get时引发的。但是,对于void返回类型,异常是未捕获的,无法传输。您可以提供AsyncUncaughtExceptionHandler来处理此类异常

public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // handle exception
    }
}
4.5 The task Namespace(task名称空间)

从3.0版开始,Spring包含一个用于配置TaskExecutor和TaskScheduler实例的XML命名空间。它还提供了一种方便的方式来配置要使用触发器调度的任务。

4.5.1 The ‘scheduler’ Element

1.以下元素创建具有指定线程池大小的ThreadPoolTaskScheduler实例:为id属性提供的值用作池中线程名称的前缀。scheduler元素相对简单。如果不提供池大小属性,则默认线程池只有一个线程。调度器没有其他配置选项。

<task:scheduler id="scheduler" pool-size="10"/>
4.5.2 The executor Element

1.为id属性提供的值用作池中线程名称的前缀。就池大小而言,executor元素比scheduler元素支持更多的配置选项。首先,ThreadPoolTaskExecutor的线程池本身更具可配置性。执行器的线程池的核心和最大大小可以有不同的值,而不仅仅是单个大小。如果只提供一个值,那么executor有一个固定大小的线程池(core和max大小相同)。但是,executor元素的pool size属性也接受一个min-max形式的范围。

2.还提供了队列容量值(queue-capacity)。线程池的配置也应该根据执行器的队列容量来考虑有关池大小和队列容量之间关系的完整描述,请参阅ThreadPoolExecutor的文档其主要思想是,当一个任务被提交时,如果当前活动线程的数量小于核心大小,则执行器首先尝试使用一个空闲线程。如果已达到核心大小,则只要任务的容量还没有达到,就将其添加到队列中。只有到那时,如果队列的容量已经达到,执行器才会创建一个超出核心大小的新线程。如果已达到最大大小,则执行者拒绝该任务。默认情况下,队列是无限的,但这很少是理想的配置,因为如果在所有池线程都繁忙的情况下向该队列添加足够多的任务,可能会导致OutOfMemory错误。此外,如果队列是无限的,那么最大大小根本没有影响。由于执行器总是在创建超出核心大小的新线程之前尝试队列,因此队列必须具有有限的容量,线程池才能超出核心大小(这就是为什么在使用无限队列时,固定大小的池是唯一合理的情况)。

3.考虑一个任务被拒绝时的情况。默认情况下,当任务被拒绝时,线程池执行器抛出TaskRejectedException。然而,拒绝策略实际上是可配置的。使用默认拒绝策略(即AbortPolicy实现)时会引发异常。对于某些任务可能在重载(heavy load)情况下被跳过的应用程序,您可以改为配置DiscardPolicy或DiscardOldTestPolicy。另一个适用于需要在重载(heavy load)情况下限制提交任务的应用程序的选项是CallerRunPolicy。该策略不会引发异常或丢弃任务,而是强制调用submit方法的线程自己运行任务。其思想是这样的调用方在运行该任务时很忙,无法立即提交其他任务。因此,它提供了一种简单的方法来控制传入负载,同时保持线程池和队列的限制。通常,这允许执行者“赶上”正在处理的任务,从而释放队列、池或两者中的一些容量。您可以从executor元素的拒绝策略属性的可用值枚举中选择这些选项中的任何一个。

4.keep-alive设置决定了线程在停止之前可以保持空闲的时间限制(以秒为单位)。如果池中的线程数超过了当前的核心线程数,那么在等待这段时间而不处理任务后,多余的线程将被停止。如果时间值为零会导致多余的线程在执行任务后立即停止,而不会在任务队列中保留后续工作

<task:executor id="executor" pool-size="10"/>
<task:executor
        id="executorWithPoolSizeRange"
        pool-size="5-25"
        queue-capacity="100"/>
<task:executor
        id="executorWithCallerRunsPolicy"
        pool-size="5-25"
        queue-capacity="100"
        rejection-policy="CALLER_RUNS"/>
<task:executor
        id="executorWithKeepAlive"
        pool-size="5-25"
        keep-alive="120"/>
4.5.3 The ‘scheduled-tasks’ Element

在这里插入图片描述

4.6 cron表达式

在这里插入图片描述

4.7 Using the Quartz Scheduler(使用Quartz调度器)

Quartz使用Trigger、Job和JobDetail对象来实现各种作业的调度。有关Quartz的基本概念,请参见官方文档。为了方便起见,Spring提供了两个类,可以在基于Spring的应用程序中简化使用Quartz。

这部分请详见官方文档,略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值