executor="myExecutor" scheduler="myScheduler"/>
id="myExecutor" pool-size="5"/>
id="myScheduler" pool-size="10"/>
Spring Framework分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。 Spring还具有支持线程池或在应用程序服务器环境中委托给CommonJ的接口的实现。最终,在公共接口背后使用这些实现抽象出了Java SE 5,Java SE 6和Java EE环境之间的差异。
Spring还提供了集成类,用于支持使用Timer(自1.3以来的JDK的一部分)和Quartz Scheduler(http://quartz-scheduler.org)进行调度。这两个调度程序都是使用FactoryBean设置的,它们分别具有对Timer或Trigger实例的可选引用。此外,还提供了Quartz Scheduler和Timer的便捷类,它允许您调用现有目标对象的方法(类似于正常的MethodInvokingFactoryBean操作)。
Spring的TaskExecutor接口与java.util.concurrent.Executor接口相同。事实上,最初,它存在的主要原因是在使用线程池时抽象出对Java 5的需求。该接口具有单个方法execute(Runnable task),该方法基于线程池的语义和配置接受要执行的任务。
最初创建TaskExecutor是为了给其他Spring组件提供所需的线程池抽象。诸如ApplicationEventMulticaster,JMS的AbstractMessageListenerContainer和Quartz集成之类的组件都使用TaskExecutor抽象来池化线程。但是,如果您的bean需要线程池行为,则可以根据自己的需要使用此抽象。
Spring发行版中包含许多预构建的TaskExecutor实现。很可能,你不应该需要实现自己的。
SimpleAsyncTaskExecutor 此实现不重用任何线程,而是为每次调用启动一个新线程。但是,它确实支持并发限制,该限制将阻止超出限制的任何调用,直到释放插槽为止。如果您正在寻找真正的池,请参阅下面的SimpleThreadPoolTaskExecutor和ThreadPoolTaskExecutor的讨论。
SyncTaskExecutor 此实现不会异步执行调用。相反,每次调用都在调用线程中进行。它主要用于不需要多线程的情况,例如简单的测试用例。
ConcurrentTaskExecutor 此实现是java.util.concurrent.Executor对象的适配器。还有一个替代方案ThreadPoolTaskExecutor,它将Executor配置参数公开为bean属性。很少需要使用ConcurrentTaskExecutor,但如果ThreadPoolTaskExecutor不够灵活,无法满足您的需求,则ConcurrentTaskExecutor 是另一种选择。
SimpleThreadPoolTaskExecutor 这个实现实际上是Quartz的SimpleThreadPool的子类,它监听Spring的生命周期回调。 当您有一个可能需要由Quartz和非Quartz组件共享的线程池时,通常会使用此方法。
ThreadPoolTaskExecutor 此实现是最常用的实现。 它公开了bean属性,用于配置java.util.concurrent.ThreadPoolExecutor并将其包装在TaskExecutor中。 如果需要适应不同类型的java.util.concurrent.Executor,建议您使用ConcurrentTaskExecutor。
WorkManagerTaskExecutor CommonJ是BEA和IBM共同开发的一组规范。这些规范不是Java EE标准,而是BEA和IBM的Application Server实现的标准。 此实现使用CommonJ WorkManager作为其后备实现,并且是在Spring上下文中设置CommonJ WorkManager引用的中心便利类。与SimpleThreadPoolTaskExecutor类似,此类实现WorkManager接口,因此也可以直接用作WorkManager。
Spring的TaskExecutor实现用作简单的JavaBeans。在下面的示例中,我们定义了一个使用ThreadPoolTaskExecutor异步打印出一组消息的bean.
如您所见,您不是从池中检索线程并自行执行,而是将Runnable添加到队列中,TaskExecutor使用其内部规则来决定何时执行任务。 要配置TaskExecutor将使用的规则,已公开简单的bean属性。
public interface TaskScheduler {
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
}
如您所见,TriggerContext是最重要的部分。它封装了所有相关数据,如有必要,将来可以进行扩展。
TriggerContext是一个接口(默认情况下使用SimpleTriggerContext实现)。在这里,您可以看到Trigger实现可用的方法。
public interface TriggerContext {
Date lastScheduledExecutionTime();
Date lastActualExecutionTime();
Date lastCompletionTime();
}
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
** "0 0 * * * *" = the top of every hour of every day. * "*/10 * * * * *" = every ten seconds. * "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day. * "0 0 6,19 * * *" = 6:00 AM and 7:00 PM every day. * "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day. * "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays * "0 0 0 25 12 ?" = every Christmas Day at midnight *
https://www.cnblogs.com/javahr/p/8318728.html(cron表达式详解)
另一个开箱即用的实现是PeriodicTrigger,它接受固定周期,可选的初始延迟值和布尔值,以指示周期是应该被解释为固定速率还是固定延迟。由于TaskScheduler接口已经定义了以固定速率或固定延迟来调度任务的方法,因此应尽可能直接使用这些方法。 PeriodicTrigger实现的值是它可以在依赖于Trigger抽象的组件中使用。例如,允许定期触发,基于cron的触发器甚至自定义触发器实现可以互换使用可能是方便的。这样的组件可以利用依赖性注入,以便可以在外部配置这样的触发器,从而容易地修改或扩展。
在Application Server环境中运行时,它提供的灵活性尤为重要,因为应用程序本身不应直接创建线程。对于这种情况,Spring提供了一个TimerManagerTaskScheduler,它委托给CommonJ TimerManager实例,通常配置有JNDI查找。
只要不需要外部线程管理,就可以使用更简单的替代方案ThreadPoolTaskScheduler。在内部,它委托给ScheduledExecutorService实例。 ThreadPoolTaskScheduler实际上也实现了Spring的TaskExecutor接口,因此单个实例可以尽快用于异步执行以及计划和可能重复执行。
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
您可以自由选择适合您应用的相关注释。例如,如果您只需要支持@Scheduled,则只需省略@EnableAsync
如果您更喜欢XML配置,请使用该元素。
executor="myExecutor" scheduler="myScheduler"/>
id="myExecutor" pool-size="5"/>
id="myScheduler" pool-size="10"/>
请注意,上述XML提供了一个执行程序引用,用于处理与带有@Async注释的方法相对应的任务,并提供了调度程序引用来管理使用@Scheduled注释的方法。
处理@Async注释的默认建议模式是“proxy”,它允许仅通过代理拦截调用;同一类中的本地调用不能以这种方式截获。对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到“aspectj”模式。
带注释的方法必须没有参数。, 它通常会有* {@code void}返回类型;, 如果不是,则通过调度程序调用时将忽略返回的值*。 , * *
{@code @Scheduled}注释的处理是通过*注册{@link ScheduledAnnotationBeanPostProcessor}来执行的。, 这可以通过{@code
此注释可用作元注释 em>,以创建具有属性覆盖的自定义* 组合注释 em>。)
@Scheduled注释可以与触发器元数据一起添加到方法中。例如,以固定延迟每5秒调用以下方法,这意味着将从每个前一次调用的完成时间开始测量该时间段。 对于固定延迟和固定速率任务,可以指定初始延迟,指示在第一次执行该方法之前等待的毫秒数。 如果简单的周期性调度不够表达,则可以提供cron表达式。例如,以下内容仅在工作日执行。 您还可以使用zone属性指定解析cron表达式的时区。 如果简单的周期性调度不够表达,则可以提供cron表达式。例如,以下内容仅在工作日执行。请注意,要调度的方法必须具有void返回值,并且不得指望任何参数。如果该方法需要与Application Context中的其他对象进行交互,则通常会通过依赖注入提供这些对象。 从Spring Framework 4.3开始,任何范围的bean都支持@Scheduled方法。 确保您没有在运行时初始化同一@Scheduled注释类的多个实例 Scheduled方法的结果被调用两次。 甚至可以异步调用返回值的方法。但是,这些方法需要具有Future类型的返回值。这仍然提供了异步执行的好处,以便调用者可以在调用Future上的get()之前执行其他任务。 CompletableFuture的:更丰富的互动使用异步任务并通过进一步的处理步骤立即组合。 @Async不能与生命周期回调一起使用,例如@PostConstruct。要异步初始化Spring bean,您当前必须使用单独的初始化Spring bean,然后在目标上调用@Async带注释的方法。 @Async没有直接的XML等价物,因为这些方法应该首先设计用于异步执行,而不是外部重新声明为异步。但是,您可以使用Spring AOP手动设置Spring的AsyncExecutionInterceptor,并结合自定义切入点。 在这种情况下,‘’otherExecutor”可以是Spring容器中任何Executor bean的名称,也可以是与任何Executor关联的限定符的名称,例如:与元素或Spring的@Qualifier注释一起指定。 默认情况下,只记录异常。可以通过AsyncConfigurer或task:annotation-driven XML元素定义自定义AsyncUncaughtExceptionHandler。 为'id'属性提供的值将用作池中线程名称的前缀。 'scheduler'元素相对简单。如果未提供“pool-size”属性,则默认线程池将只有一个线程。调度程序没有其他配置选项。 与上面的调度程序一样,为“id”属性提供的值将用作池中线程名称的前缀。就池大小而言,'executor'元素支持比'scheduler'元素更多的配置选项。首先,ThreadPoolTaskExecutor的线程池本身更易于配置。执行程序的线程池可能具有不同的核心值和最大大小,而不仅仅是单个大小。如果提供单个值,则执行程序将具有固定大小的线程池(核心和最大大小相同)。但是,'executor'元素的'pool-size'属性也接受“min-max”形式的范围。 从该配置中可以看出,还提供了“队列容量”值。还应根据执行程序的队列容量来考虑线程池的配置。有关池大小和队列容量之间关系的完整描述,请参阅ThreadPoolExecutor的文档。主要思想是,当提交任务时,如果活动线程的数量当前小于核心大小,则执行程序将首先尝试使用空闲线程。如果已达到核心大小,则只要尚未达到其容量,任务就会添加到队列中。只有这样,如果已达到队列的容量,执行程序是否会创建超出核心大小的新线程。如果还达到了最大大小,则执行程序将拒绝该任务。 默认情况下,队列是无限制的,但这很少是所需的配置,因为如果在所有池线程忙的情况下将足够的任务添加到该队列,则可能导致OutOfMemoryErrors。此外,如果队列是无界的,那么最大大小根本没有影响。由于执行器将始终在创建超出核心大小的新线程之前尝试队列,因此队列必须具有有限的容量,以使线程池增长超出核心大小(这就是为什么固定大小的池是使用时唯一合理的情况一个无限的队列)。 稍后,我们将回顾保持活动设置的效果,这增加了在提供池大小配置时要考虑的另一个因素。首先,如上所述,让我们考虑一个任务被拒绝的情况。默认情况下,当任务被拒绝时,线程池执行程序将抛出TaskRejectedException。但是,拒绝策略实际上是可配置的。使用默认拒绝策略(AbortPolicy实现)时会抛出异常。对于可以在高负载下跳过某些任务的应用程序,可以配置DiscardPolicy或DiscardOldestPolicy。另一个适用于需要在高负载下限制提交的任务的应用程序的选项是CallerRunsPolicy。该策略不会抛出异常或丢弃任务,而只是强制调用submit方法的线程自己运行任务。这个想法是这样的调用者在运行该任务时会很忙,而不能 立即提交其他任务。 因此,它提供了一种简单的方法来限制传入的负载,同时保持线程池和队列的限制。 通常,这允许执行程序“赶上”它正在处理的任务,从而释放队列,池中或两者中的一些容量。 可以从'executor'元素上'rejection-policy'属性的可用值枚举中选择任何这些选项。 最后,keep-alive设置确定线程在终止之前可以保持空闲的时间限制(以秒为单位)。如果池中当前有多个线程核心数,则在等待这段时间而不处理任务后,多余的线程将被终止。时间值为零将导致多余线程在执行任务后立即终止,而不会在任务队列中保留后续工作。
Spring的任务命名空间最强大的功能是支持在Spring Application Context中配置要安排的任务。这遵循类似于Spring中的其他“方法调用者”的方法,例如由JMS名称空间提供的用于配置消息驱动的POJO的方法。基本上,“ref”属性可以指向任何Spring管理的对象,“method”属性提供要在该对象上调用的方法的名称。这是一个简单的例子。 上面的例子将导致在exampleBusinessObject方法上调用doIt方法(见下文): 我们已经创建了工作细节和jobs。 我们还回顾了允许您在特定对象上调用方法的便捷bean。 当然,我们仍然需要自己安排工作。 这是使用触发器和SchedulerFactoryBean完成的。 Quartz中提供了几个触发器,Spring提供了两个带有方便默认值的Quartz FactoryBean实现:CronTriggerFactoryBean和SimpleTriggerFactoryBean。
如果需要固定费率执行,只需更改注释中指定的属性名称即可。在每次调用的连续开始时间之间测量的每5秒执行以下操作。
@Scheduled(fixedDelay=5000)/*固定延迟**/
/**在最后一次调用的结束和下一次调用的开始之间以固定的周期(以毫秒为单位)执行带注释的方法。 @return延迟,以毫秒为单位**/
public void doSomething() {
// something that should execute periodically
}
@Scheduled(fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should execute on weekdays only
}
,除非您确实要为每个此类实例安排回调。与此相关,请确保不对使用@Scheduled注释的bean类使用@Configurable,
并将其注册为带有容器的常规Spring bean:否则将获得双初始化,一次通过容器,一次通过@Configurable方面,每个@ 4.3@Async注解
可以在方法上提供@Async注释,以便异步调用该方法。换句话说,调用者将在调用时立即返回,
并且该方法的实际执行将发生在已提交给Spring TaskExecutor的任务中。在最简单的情况下,注释可以应用于返回空隙的方法。
与使用@Scheduled注释注释的方法不同,这些方法可以使用参数,因为它们将在运行时由调用者以“正常”方式调用,而不是由容器管理的调度任务调用。例如,以下是@Async注释的合法应用程序。
@Async
void doSomething() {
// this will be executed asynchronously
}
@Async
void doSomething(String s) {
// this will be executed asynchronously
}
@Async方法不仅可以申报一个普通java.util.concurrent.Future返回类型,也可以是Spring的org.springframework.util.concurrent.@Async
Future<String> returnSomething(int i) {
// this will be executed asynchronously
}
ListenableFuture,或如spring4.2,JDK 8的java.util.concurrent.
4.4使用@Async执行者资格
@Async("otherExecutor")
void doSomething(String s) {
// this will be executed asynchronously by "otherExecutor"
}
4.5使用@Async进行异常管理
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}
5.任务命名空间
5.1 scheduler元素
以下元素将创建具有指定线程池大小的ThreadPoolTaskScheduler实例。
5.2 executor元素
5.3 scheduled-tasks元素
quartz-scheduler.org。为方便起见,Spring提供了几个类,简化了基于Spring的应用程序中Quartz的使用。
6.1 使用JobDetailFactoryBean
当然,您也可以使用作业数据图中的所有其他属性。使用名称和组属性,您可以分别修改作业的名称和组。默认情况下,作业的名称与JobDetailFactoryBean的bean名称匹配(在上面的示例中,这是exampleJob)。
package example;
public class ExampleJob extends QuartzJobBean {
private int timeout;
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailFactoryBean (5)
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
// do the actual work
}
}
6.2 使用MethodInvokingJobDetailFactoryBean
通常,您只需要在特定对象上调用方法。使用MethodInvokingJobDetailFactoryBean可以完成以下操作:
public class ExampleBusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
默认情况下,Quartz Jobs是无状态的,导致作业相互干扰的可能性。 如果为同一JobDetail指定两个触发器,则可能在第一个作业完成之前,第二个作业将启动。 如果JobDetail类实现Stateful接口,则不会发生这种情况。 第二个作业在第一个作业完成之前不会开始。 要使MethodInvokingJobDetailFactoryBean产生的作业非并发,请将concurrent标志设置为false。
6.3 使用触发器和SchedulerFactoryBean连接jobs
需要安排触发器。 Spring提供了一个SchedulerFactoryBean,它公开了要设置为属性的触发器。 SchedulerFactoryBean使用这些触发器调度实际jobs。
bean="cronTrigger"/>
bean="simpleTrigger"/>