内容简介:1 概述Spring框架分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。Spring还提供了这些接口的实现,这些接口支持线程池或将其委托给应用服务器环境中的CommonJ。2 TaskExecutor
| 编辑推荐: |
| 本文来自于csdn,本文主要介绍Spring框架在异步执行和任务调度方面的相关内容,希望对大家能有所帮助。 |
1 概述
Spring框架分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。Spring还提供了这些接口的实现,这些接口支持线程池或将其委托给应用服务器环境中的CommonJ。
2 TaskExecutor
Spring 2.0 开始引入的新的抽像。Executors 是线程池的 Java 5名称。之所以称作是“执行器”是因为不能保证底层实现实际上是一个池;执行程序可以是单线程的,甚至是同步的。Spring的TaskExecutor接口与java.util.concurrent是等价的。
2.1 TaskExecutor类型
SimpleAsyncTaskExecutor
线程不会重用,每次调用开启一个新的线程。支持并发,超过最大并发调用数时,会阻塞,直到释放一个槽为止。
SyncTaskExecutor
不会异步执行调用。每次调用都发生在调用线程中。它主要用于不需要多线程的情况。
ConcurrentTaskExecutor
Java 5 Java .util.concurrent. executor的包装。替代方案是ThreadPoolTaskExecutor,它将Executor配置参数作为bean属性公开。很少使用。
SimpleThreadPoolTaskExecutor
Quartz的SimpleThreadPool的一个子类,它监听Spring的生命周期回调。Quartz组件和非Quartz组件共享需要共享一个线程池时,通常会使用这种方法。
ThreadPoolTaskExecutor
只能在java5中使用。公开了用于配置java.util.concurrent的bean属性。如果需要高级的东西,比如ScheduledThreadPoolExecutor,建议使用ConcurrentTaskExecutor替代。
TimerTaskExecutor
通过TimerTask支撑实现。 不同于SyncTaskExecutor,因为方法调用在一个单独的线程中执行,尽管它们在那个线程中是同步的。
WorkManagerTaskExecutor
使用CommonJ WorkManager作为它的支持实现,并且是在Spring上下文中设置CommonJ WorkManager引用的中心便利类。与SimpleThreadPoolTaskExecutor类似,这个类实现了WorkManager接口,因此也可以直接作为WorkManager使用。
2.2 使用 TaskExecutor
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));
}
}
}
与其从池中检索线程并自己执行,不如将Runnable添加到队列中,而TaskExecutor使用其内部规则来决定任务何时执行
配置TaskExecutor将使用的规则
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent .ThreadPoolTaskExecutor"> <property name="corePoolSize" value="5" /> <property name="maxPoolSize" value="10" /> <property name="queueCapacity" value="25" /> </bean> <bean id="taskExecutorExample" class="cn.pconline.activity.task.TaskExecutorExample" init-method="printMessages"> <constructor-arg ref="taskExecutor" /> </bean>
3 TaskScheduler
除了任务执行者抽象之外。Spring 3.0还引入了一个TaskScheduler,它有多种方法来调度未来某个时候运行的任务。
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);
}
名为“schedule”的方法,它只接受可运行的日期。这将导致任务在指定时间后运行一次。
所有其他方法都能够安排任务重复运行。固定速率和固定延迟方法用于简单的、周期性的执行,但是使用 Trigger 的方法要灵活得多。
3.1 Trigger
触发器的基本思想是,执行时间可以根据过去的执行结果甚至任意条件来确定。如果这些决定确实考虑了前面执行的结果,那么该信息在TriggerContext中是可用的。
public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
}
public interface TriggerContext {
Date lastScheduledExecutionTime();
Date lastActualExecutionTime();
Date lastCompletionTime();
}
TriggerContext是最重要的部分。它封装了所有相关的数据,如果有必要,将来还可以进行扩展。TriggerContext是一个接口(默认情况下使用SimpleTriggerContext实现)。
3.2 Trigger 实现
Spring提供了触发器接口的两个实现。最有趣的是CronTrigger。它支持基于cron表达式的任务调度。
另一个开箱即用的实现是一个周期性Trigger ,它接受一个固定的周期、一个可选的初始延迟值,以及一个布尔值,用来指示周期应该解释为固定速率还是固定延迟。由于TaskScheduler接口已经定义了以固定速率或固定延迟调度任务的方法,因此应该尽可能直接使用这些方法。PeriodicTrigger实现的价值在于它可以在依赖于触发器抽象的组件中使用。例如,允许周期性Trigger 、基于cro的Trigger ,甚至自定义Trigger 实现可以互换使用,这可能很方便。这样的组件可以利用依赖注入,这样就可以在外部配置这样的Trigger 。
3.3 TaskScheduler实现
在应用服务器环境中,TaskScheduler提供的灵活性尤其重要。因为在这种环境中,线程不应该由应用程序本身直接创建。对于这种情况,Spring提供了一个TimerManagerTaskScheduler,它将委托给CommonJ TimerManager实例,通常配置为JNDI-lookup。
4 调度和异步执行的注解支持
4.1 开启scheduling 注解功能
为了支持@Scheduled和@Async注释,请将@EnableScheduling和@EnableAsync添加到@Configuration类中
@Configuration
@EnableAsync
@EnableSCheduling
public class AppConfig {
}
你可以自由选择应用程序的相关注释。例如,如果只需要支持@Scheduled,那么只需省略**@EnableAsync即可。对于更细粒度的控制,可以另外实现调度器和/或AsyncConfigurer**接口。
如果你更喜欢xml配置,这样配置。
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>}
4.2 @Scheduled
@Scheduled添加到方法上
//上一次调用完之后,五秒再调用一次,依此循环下去
@Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
//连续的每次调用开始时间之间,间隔5s
@Scheduled(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
}
值得注意的是调度的方法返回值必须是void,并且不能期望有任何参数。如果方法需要与来自应用程序上下文的其他对象交互,那么这些对象通常是通过依赖注入提供的。
4.3 @Async注解
添加了**@Async**注解的方法会异步执行。换句话说,方法调用后会立即返回,方法的实际执行将发生在提交给Spring TaskExecutor的任务中。
@Async
void doSomething() {
// this will be executed asynchronously
}
/@Scheduled注释的方法不同,这些方法可以预期参数.
//因为调用方将在运行时以“正常”方式调用它们,而不是从容器管理的调度任务中调用。
@Async
void doSomething(String s) {
// this will be executed asynchronously
}
// 具有Future回调返回值
//执行其它任务的优先级 依然是高于执行回调的优先级。
@Async
Future<String> returnSomething(int i) {
// this will be executed asynchronously
}
4.4 指定@Async注解的执行器
默认情况下,在方法上指定@Async时,将使用的执行器是提供“annotation-driven”元素的执行器,如上所述。然而,当需要指示在执行给定方法时应该使用非默认的执行器时,可以使用**@Async**注释的值属性。
@Async("otherExecutor")
void doSomething(String s) {
// this will be executed asynchronously by "otherExecutor"
}
5 Task 命名空间
从Spring 3.0开始,有一个用于配置TaskExecutor和TaskScheduler实例的XML名称空间。并提供了一种方便的方法,可以将任务配置为使用触发器进行调度。
5.1 scheduler 元素
<!-- 默认创建一个指定大小的ThreadPoolTaskScheduler -->
<task:scheduler id="scheduler" pool-size="10"/>
id属性用作线程池中线程的前缀名。如果不指定pool-size,默认的线程池中只有一个线程。
5.2 executor元素
<!-- 创建ThreadPoolTaskExecutor实例 -->
<task:executor id="executor" pool-size="10"/>
与上面的调度器一样,为’id’属性提供的值将用作池中线程名称的前缀。就池大小而言,'executor’元素比’scheduler’元素支持更多的配置选项。首先,ThreadPoolTaskExecutor的线程池本身是可配置的。执行程序的线程池可能对核心和最大大小有不同的值,而不仅仅是单个大小。如果提供了单个值,那么执行器将拥有一个固定大小的线程池(核心和最大大小相同)。然而,“executor”元素的“池大小”属性也接受“min-max”形式的范围。
<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"/><!-- 任务拒绝策略(强制任务提交线程执行任务,从而限制传入负载变得过大) 默认的是直接抛出异常-->
5.3 scheduled-tasks元素
Spring task namespace 最强大的特性是支持在Spring应用程序上下文中配置要调度的任务。这与Spring中的其他“方法调用者”类似,例如JMS名称空间提供的配置消息驱动pojo的方法。
<task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="methodA" fixed-delay="5000"/><!--ref 指向容器管理的对象,method是要执行的方法名--> </task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="10"/>
<task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/><!--第一次执行延迟一秒,以后每次上一个任务完成后 再执行--> <task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/> <!--固定频率执行 如果上一个任务执行时间超时,第二个任务会立即执行 --> <task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/> </task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="10"/>
6 Quartz Scheduler
Quartz使用触发器、作业和作业细节对象来实现各种作业的调度。有关Quartz背后的基本概念,请参阅http://quartz-scheduling er.org。为了方便起见,Spring提供了两个类,它们简化了基于Spring的应用程序中Quartz的使用。
6.1 使用JobDetailBean
JobDetail对象包含运行作业所需的所有信息。Spring框架提供了JobDetailBean,它使JobDetail更接近于具有合理默认值的实际JavaBean。
<bean name="exampleJob" class="org.springframework.scheduling .quartz.JobDetailBean"> <property name="jobClass" value="example.ExampleJob" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="5" /> </map> </property> </bean>
job detail bean 具有运行作业(ExampleJob)所需的所有信息。timeout在job data map指定。job data map可以通过JobExecutionContext(在执行时传递给您)获得,但是JobDetailBean还将 job data map 中的属性映射到实际job的属性。因此,在本例中,如果ExampleJob包含一个名为timeout的属性,JobDetailBean将自动应用它。
package example;
public class ExampleJob extends QuartzJobBean {
private int timeout;
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailBean (5)
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
// do the actual work
}
}
6.2 使用MethodInvokingJobDetailFactoryBean
使用MethodInvokingJobDetailFactoryBean你可以调用特定对象上的方法。
<bean id="jobDetail" class="org.springframework.scheduling.quartz .MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> </bean>
使用上面的配置将会导致ExampleBusinessObject .doIt()方法被调用。
public class ExampleBusinessObject {
// properties and collaborators
public void doIt() {
// do the actual work
}
}
使用MethodInvokingJobDetailFactoryBean,不再需要创建只调用一个方法的一行作业,只需要创建实际的业务对象并连接到它。
默认情况下,Quartz作业是无状态的,导致作业相互干扰的可能性。如果为相同的JobDetail指定两个触发器,那么可能在第一个作业完成之前,第二个作业就会开始。如果JobDetail类实现有状态接口,则不会发生这种情况。在第一项工作完成之前,第二项工作不会开始。要使方法调用jobdetailfactorybean产生的作业非并发,请将并发标志设置为false。
<bean id="jobDetail" class="org.springframework.scheduling.quartz .MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> <property name="concurrent" value="false" /> <!--并发执行false--> </bean>
6.3 作业调度
尽管我们能使用MethodInvokingJobDetailFactoryBean调用特定对象上的方法,但是我们还是需要调度作业 。这需要使用触发器和scheduler erfactorybean完成。Quartz 提供了多种触发器
Spring提供了两种Quartz 工厂对象:
CronTriggerFactoryBean
SimpleTriggerFactoryBean
两种触发器的示例
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz .SimpleTriggerFactoryBean"> <!-- see the example of method invoking job above --> <property name="jobDetail" ref="jobDetail" /> <!-- 10 seconds --> <property name="startDelay" value="10000" /> <!-- repeat every 50 seconds --> <property name="repeatInterval" value="50000" /> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz .CronTriggerFactoryBean"> <property name="jobDetail" ref="exampleJob" /> <!-- run every morning at 6 AM --> <property name="cronExpression" value="0 0 6 * * ?" /> <!-- 每天早上六点执行--> </bean>
关于SchedulerFactoryBean的等多属性设置 ,参考SchedulerFactoryBean javadoc
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang 源码学习调度逻辑(三):工作线程的执行流程与调度循环
- EB 级系统空中换引擎:阿里调度执行框架如何全面升级?
- 理解golang调度之一 :操作系统调度
- 理解golang调度之二 :Go调度器
- Node.js CPU调度优化(多服务器多核心分配调度)
- Hadoop 容器调度器与公平调度器原理和实践深入剖析-Hadoop商业环境实战
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!