网站首页 > 精选教程 正文
环境:Spring5.3.23
概述
Spring框架分别通过TaskExecutor和TaskScheduler接口为任务的异步执行和调度提供了抽象。Spring还提供了在应用服务器环境中支持线程池或向CommonJ委托的那些接口的实现。最终,在公共接口背后使用这些实现可以抽象出Java SE 5、Java SE 6和Java EE环境之间的差异。
Spring还提供了集成类来支持Timer(1.3以来JDK的一部分)和Quartz调度器(https://www.quartz-scheduler.org/)的调度。你可以分别使用一个FactoryBean和对Timer或Trigger实例的可选引用来设置这两个调度器。此外,Quartz调度器和计时器都有一个方便类,它允许你调用现有目标对象的方法(类似于普通的MethodInvokingFactoryBean操作)。
TaskExecutor抽象
执行器是JDK中线程池概念的名称。命名为“执行器”是因为无法保证底层实现实际上是一个池。执行器进程可以是单线程的,甚至可以是同步的。Spring的抽象隐藏了Java SE和Java EE环境之间的实现细节。
Spring的TaskExecutor接口是java.util.concurrent.Executor接口子类。它最初存在的主要原因是在使用线程池时抽象出对Java 5的需求。该接口有一个方法(execute(Runnable task)),该方法根据线程池的语义和配置接受一个要执行的任务。
TaskExecutor最初是为其他Spring组件提供线程池抽象的。ApplicationEventMulticaster、JMS的AbstractMessageListenerContainer和Quartz集成等组件都使用TaskExecutor抽象来共享线程。然而,如果bean需要线程池行为,也可以根据自己的需要使用这种抽象。
TaskExecutor类型
Spring包含了许多预构建的TaskExecutor实现。十有八九,你永远都不需要自己实现。Spring提供的变体如下。
- SyncTaskExecutor
这个实现不会异步运行调用。相反,每次调用都发生在调用线程中。它主要用于不需要多线程的情况,例如简单的测试用例。
- SimpleAsyncTaskExecutor
这个实现没有重用任何线程。相反,它为每次调用启动一个新线程。但它确实支持并发限制,该限制会阻塞任何超过并发限制的调用,直到释放一个任务槽。参考:ThreadPoolTaskExecutor。
- ConcurrentTaskExecutor
这个实现是java.util.concurrent.Executor实例的一个适配器。有一个替代方案(ThreadPoolTaskExecutor)将执行器配置参数暴露为bean属性。很少需要直接使用ConcurrentTaskExecutor。但是,如果ThreadPoolTaskExecutor不够灵活,不能满足需求,那么ConcurrentTaskExecutor是一种替代方案。
- ThreadPoolTaskExecutor
这种实现是最常用的。它公开bean属性,用于配置java.util.concurrent.ThreadPoolExecutor,并将其包装在TaskExecutor中。如果你需要适应另一种java.util.concurrent.Executor,建议你使用ConcurrentTaskExecutor。
- WorkManagerTaskExecutor
这个实现使用CommonJ WorkManager作为它的后台服务提供者,它是用于在Spring应用程序上下文中的WebLogic或WebSphere上设置基于CommonJ的线程池集成的主要便利类。
- DefaultManagedTaskExecutor
此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用jndi获得的ManagedExecutorService,取代了CommonJ WorkManager。
使用TaskExecutor
Spring的TaskExecutor实现被用作简单的JavaBeans。在下面的例子中,我们定义了一个bean,它使用ThreadPoolTaskExecutor异步打印出一组消息:
import org.springframework.core.task.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));
}
}
}
正如所看到的,你将可运行线程添加到队列中,而不是从线程池中检索线程并自己执行它。然后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="TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>
TaskScheduler抽象
除了TaskExecutor抽象之外,Spring 3.0还引入了TaskScheduler,它提供了各种调度任务的方法,以便在未来的某个时候运行。下列代码清单给出了TaskScheduler接口的定义:
public interface TaskScheduler {
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Instant startTime);
ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
最简单的方法是名为schedule的方法,它只接受一个Runnable对象和一个Date对象。这会导致任务在指定的时间之后运行一次。所有其他方法都能够调度任务重复运行。固定速率和固定延迟方法用于简单的周期性执行,但接受触发器的方法要灵活得多。
Trigger 接口
Trigger接口本质上是受JSR-236启发的,到Spring 3.0时还没有正式实现。Trigger的基本思想是,执行时间可以根据过去的执行结果甚至任意条件来确定。如果这些确定确实考虑了前面执行的结果,那么该信息在TriggerContext中是可用的。Trigger接口本身非常简单,如下列代码清单所示:
public interface Trigger {
Date nextExecutionTime(TriggerContext triggerContext);
}
TriggerContext是最重要的部分。它封装了所有相关的数据,如果有必要,将来可以进行扩展。TriggerContext是一个接口(默认使用SimpleTriggerContext实现)。下列代码清单展示了触发器实现中可用的方法。
public interface TriggerContext {
Date lastScheduledExecutionTime();
Date lastActualExecutionTime();
Date lastCompletionTime();
}
Trigger实现
Spring提供了Trigger接口的两种实现。最有趣的是CronTrigger。支持基于cron表达式的任务调度。例如,下面的任务计划每小时运行15分钟,但只在工作日朝九晚五的“工作时间”:
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
另一种实现是一个周期性触发器,它接受一个固定的周期、一个可选的初始延迟值和一个布尔值,表示该周期应该被解释为固定速率还是固定延迟。由于TaskScheduler接口已经定义了以固定速率或固定延迟调度任务的方法,因此应该尽可能直接使用这些方法。PeriodicTrigger实现的价值在于,您可以在依赖于触发器抽象的组件中使用它。例如,允许周期性触发器、基于cron的触
发器甚至自定义触发器的实现交替使用可能会很方便。这样的组件可以利用依赖注入,这样您就可以在外部配置这样的触发器,从而轻松地修改或扩展它们。
TaskScheduler 实现
与Spring的TaskExecutor抽象一样,TaskScheduler安排的主要好处是应用程序的调度需求与部署环境解耦了。在部署到应用服务器环境时,这个抽象级别特别重要,因为应用程序本身不应该直接创建线程。对于这样的场景,Spring提供了一个TimerManagerTaskScheduler,它将任务委托给WebLogic或WebSphere上的CommonJ TimerManager,以及一个最新的DefaultManagedTaskScheduler,它将任务委托给Java EE 7+环境中的JSR-236 ManagedScheduledExecutorService。两者通常都配置了JNDI查找。
当不需要外部线程管理时,一种更简单的替代方案是在应用程序中设置本地ScheduledExecutorService,可以通过Spring的concurrentaskscheduler进行调整。为了方便起见,Spring还提供了ThreadPoolTaskScheduler,它在内部委托给ScheduledExecutorService来提供常见的bean风格的配置,类似于ThreadPoolTaskExecutor。在宽松的应用服务器环境中,这些变体非常适合本地嵌入式线程池设置,特别是在Tomcat和Jetty上。
完毕!!!
下一篇将会介绍如何使用异步执行及任务调度。
SpringBoot对Spring MVC都做了哪些事?(一)
SpringBoot对Spring MVC都做了哪些事?(二)
SpringBoot对Spring MVC都做了哪些事?(三)
SpringBoot对Spring MVC都做了哪些事?(四)
Spring Retry重试框架的应用
spring data jpa 高级应用
- 上一篇: JAVA新手入门必备功课~安装JDK,你学会了吗
- 下一篇: Java 设计模式之享元模式介绍
猜你喜欢
- 2025-02-04 JAVA新手入门必备功课~安装JDK,你学会了吗
- 2025-02-04 【每日一学】零基础起航:JavaSE面向对象编程入门指南!
- 2025-02-04 Oralce JDK和Open JDK的区别与联系
- 2025-02-04 Java基础知识总结(堪称经典)(“java基础知识点”)
- 2025-02-04 Java 一周速递:JDK 24 即将推出、JDK 25 专家组、Jakarta EE 11 简介等
- 2025-02-04 搞懂Java本地事务和分布式事务(分布式事务与本地事务的区别)
- 2025-02-04 jvm 性能调优工具之 jstat 命令详解
- 2025-02-04 Mycat入门(mycat教程)
- 2025-02-04 SpringBoot 整合 Quartz 实现 JAVA 定时任务的动态配置
- 2025-02-04 java自学—各阶段教程(java入门教程自学网)
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)