前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring任务调度scheduled_golang 任务调度

spring任务调度scheduled_golang 任务调度

作者头像
全栈程序员站长
发布2022-11-07 15:10:11
1.9K0
发布2022-11-07 15:10:11
举报
文章被收录于专栏:全栈程序员必看

任务调度接口:TaskScheduler

除了TaskExecutor抽象之外,Spring 3.0还引用了任务调度接口 TaskScheduler,它提供了多种方法来调度将来某个时间点要运行的任务。

代码语言:javascript
复制
public interface TaskScheduler { 

ScheduledFuture schedule(Runnable task, Trigger trigger); //通过触发器来决定task是否执行
ScheduledFuture schedule(Runnable task, Date startTime);  //在starttime的时候执行一次
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); //从starttime开始每个period时间段执行一次task
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);  //每隔period执行一次
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); //从startTime开始每隔delay长时间执行一次
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); //每隔delay时间执行一次
}

固定速率和固定延迟方法用于简单的、周期性的执行,但是使用 Trigger 的方法要灵活得多。

Trigger接口

TaskScheduler中将会使用到Trigger对象,Trigger接口用于计算任务的下次执行触发时间。通过实现Trigger接口可以实现自定义触发器来执行执行task。

代码语言:javascript
复制
public interface Trigger { 

Date nextExecutionTime(TriggerContext triggerContext);
}

TriggerContext 保存 Trigger 接口任务执行调度的信息。它封装了所有相关的数据,如果需要,将来可以对其进行扩展。TriggerContext是一个接口(默认情况下使用SimpleTriggerContext实现)。在这里,您可以看到哪些方法可用于触发器实现。

代码语言:javascript
复制
public interface TriggerContext { 

Date lastScheduledExecutionTime();
Date lastActualExecutionTime();
Date lastCompletionTime();
}

Trigger实现

Spring也提供了触发器接口的两个默认的实现类:PeriodicTrigger 和 CronTrigger。

PeriodicTrigger

用于定期执行的Trigger。它有两种模式:

  • fixedRate:两次任务开始时间之间间隔指定时长
  • fixedDelay: 上一次任务的结束时间与下一次任务开始时间“间隔指定时长

默认情况下PeriodicTrigger使用了fixedDelay模式

CronTrigger

通过Cron表达式来生成调度计划。比如:scheduler.schedule(task, new CronTrigger(“0 15 9-17 * * MON-FRI”)); 表示 “工作日的9点到17点,每个小时的15分执行一次”。

cron表达式含义见《cron表达式

Spring对cron表达式的支持,是由CronSequenceGenerator来实现的,不依赖于别的框架。下面给出一个Demo感受下:

代码语言:javascript
复制
public static void main(String[] args) { 

CronSequenceGenerator generator = new CronSequenceGenerator("0 15 * * * MON-FRI");
Date next = generator.next(new Date());
System.out.println(next); //Mon Apr 22 17:15:00 CST 2021
System.out.println(generator.next(next)); //Mon Apr 22 18:15:00 CST 2021
}

TaskScheduler接口

Spring任务调度器的核心接口,定义了执行定时任务的主要方法,主要根据任务的不同触发方式调用不同的执行逻辑,其实现类都是对JDK原生的定时器或线程池组件进行包装,并扩展额外的功能。

TaskScheduler实现

与Spring的TaskExecutor抽象一样,TaskScheduler 主要好处是应用程序的调度需求与部署环境解耦,应用程序本身不应该直接创建线程。

TaskScheduler有如下实现类 ConcurrentTaskScheduler、ThreadPoolTaskScheduler:

ConcurrentTaskScheduler

以单个线程方式执行定时任务,适用于简单场景。

代码语言:javascript
复制
public class testTaskExecutor { 

public static void main(String[] args) { 

ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler();
// 执行任务
// 执行一次
taskScheduler.execute(() -> System.out.println(Thread.currentThread().getName() + " 我只会被执行一次~~~"));
// 周期性执行
taskScheduler.schedule(() -> System.out.println(Thread.currentThread().getName() + " 我会被多次执行~~~"), new CronTrigger("0/2 * * * * ?"));
// 此处:若你有周期性的任务,这里不要shutdown()
//taskScheduler.shutdown();
}
}

执行结果:

执行的线程都是一样的。

ThreadPoolTaskScheduler

代码语言:javascript
复制
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport 
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler

除实现了TaskScheduler接口中的方法外,它还包含了一些对ScheduledThreadPoolExecutor进行操作的接口,大多数场景下都使用它来进行任务调度。

任务调度Demo

代码语言:javascript
复制
package TaskSchedulerDemo;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
public class testTaskExecutor { 

public static void main(String[] args) { 

ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(6);
taskScheduler.initialize(); // 务必调用此方法来手动启动
// 执行任务
// 执行一次
taskScheduler.execute(new Runnable() { 

@Override
public void run() { 

System.out.println(Thread.currentThread().getName() + " 我只会被执行一次~~~");
}
});
// lambda表达式,多用于匿名内部类、forEach()方法等。小括号()用于传参,大括号{}用于执行相关操作、返回值等。
// taskScheduler.execute(() -> System.out.println(Thread.currentThread().getName() + " 我只会被执行一次~~~"));
// 周期性执行
taskScheduler.schedule(() -> System.out.println(Thread.currentThread().getName() + " 我会被多次执行~~~"), new CronTrigger("0/2 * * * * ?"));
// 此处:若你有周期性的任务,这里不要shutdown()
//taskScheduler.shutdown();
}
}

执行结果:

注:使用前必须得先调用initialize()【初始化方法】。shutDown()方法执行完后可以关闭线程。

注入方式

1、applicationContext.xml配置文件配置ThreadPoolTaskScheduler:

代码语言:javascript
复制
<context:component-scan base-package="TaskSchedulerDemo" />
<bean id = "taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value = "5"></property>
</bean>

2、定义一个任务DataSimulation:

代码语言:javascript
复制
package TaskSchedulerDemo;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component
public class DataSimulation implements Runnable { 

@Override
public void run() { 

Random random = new Random();
System.out.println("[" + Thread.currentThread().getName() + "]" +"-" + random.nextInt(10));
}
}

3、SchedulerFacotory类注入TaskScheduler 对象:

代码语言:javascript
复制
@Component
public class SchedulerFacotory { 

@Autowired
public TaskScheduler scheduler;
public TaskScheduler getScheduler() { 

return scheduler;
}
public void setScheduler(TaskScheduler scheduler) { 

this.scheduler = scheduler;
}
public void schedulerFactory(){ 

scheduler.schedule(new DataSimulation(), new CronTrigger("0/2 * * * * ?"));
}
}

4、调用:

代码语言:javascript
复制
public class testTaskScheduler { 

public static void main(String[] args) { 

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { 
"applicationContext.xml"});
SchedulerFacotory sf = (SchedulerFacotory)context.getBean("schedulerFacotory");
sf.schedulerFactory();
}
}

运行结果:

注解方式(自动启动)

1、Spring配置文件applicationContext.xml中注解配置如下:

代码语言:javascript
复制
<!--添加注解的扫描包-->
<context:component-scan base-package="TaskSchedulerAnnoStartDemo" />
<!--配置注解驱动-->
<task:annotation-driven />
<task:scheduler id="myScheduler" pool-size="5"/>

<task:annotation-driven />还可以通过scheduler,指定具体的任务调度器。

代码语言:javascript
复制
<!--添加注解的扫描包-->
<context:component-scan base-package="TaskSchedulerAnnoStartDemo" />
<!--配置注解驱动 多个scheduler时,可以指定scheduler-->
<task:annotation-driven scheduler="myScheduler2"/>
<task:scheduler id="myScheduler" pool-size="5"/>
<bean id = "myScheduler2" class="org.springframework.scheduling.concurrent.ConcurrentTaskScheduler">
</bean>

注:<task:scheduler id=“myScheduler” pool-size=“5”/> 默认使用 ThreadPoolTaskScheduler

2、创建SchedulerPoolService,并在service中使用 @Scheduled 注解创建定时任务

代码语言:javascript
复制
package TaskSchedulerDemo;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class SchedulerPoolService { 

@Scheduled(cron = "0/3 * * * * ?")
public void task1(){ 

Thread thread =  Thread.currentThread();
System.out.println("[" + Thread.currentThread().getName() + "]" + new Date() + "-task1-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
@Scheduled(fixedDelay = 5000)
public void task2(){ 

Thread thread =  Thread.currentThread();
System.out.println("[" + Thread.currentThread().getName() + "]" + new Date() + "-task2-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
}

3、加载配置文件即可,不需要手动启动任务。

代码语言:javascript
复制
public class testTaskExecutor { 

public static void main(String[] args) { 

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { 
"TaskSchedulerDemo/applicationContext.xml"});
}
}

运行结果:

完全注解开发(自动启动)

还可以结合配置类 @Configuration,@EnableScheduling 开启配置计划任务,实现完全注解开发,不需要手动启动任务。 配置类SpringConfig:

代码语言:javascript
复制
@Configuration
@ComponentScan("TaskSchedulerAnnoStartDemo")
@EnableScheduling // 开启配置计划任务
public class SpringConfig { 

@Bean
public TaskScheduler getTaskScheduler(){ 

ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
return threadPoolTaskScheduler;
}
}

启动类:

代码语言:javascript
复制
public class testTaskExecutor { 

public static void main(String[] args) { 

//ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"TaskSchedulerAnnoStartDemo/applicationContext.xml"});
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
}
}

运行结果:

Spring异步执行

Spring默认的事件机制是同步的。举个例子:

代码语言:javascript
复制
@Service
public class SchedulerPoolService { 

@Scheduled(fixedDelay = 3000)
public void task(){ 

Thread thread =  Thread.currentThread();
try { 

Thread.sleep(5000);
} catch (InterruptedException e) { 

e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
}

在原先service增加触发时间打印,而且sleep(5000)。

运行结果:

可以看出,任务每8秒执行一次,是轮询秒数(3秒)+ 单次任务执行时间(5秒),说明任务是同步执行。

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

@Async 注解方法

有时候需要任务异步执行,不然太耗时,Spring提供注解 @Async 标注异步方法执行

代码语言:javascript
复制
@Service
public class SchedulerPoolService { 

//此注解为异步方法注解,如果注解到类上,表示此类的所有方法都为异步方法
@Async()
@Scheduled(fixedDelay = 3000)
public void task(){ 

Thread thread =  Thread.currentThread();
try { 

Thread.sleep(5000);
} catch (InterruptedException e) { 

e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
}

运行结果:

可以看出,任务每3秒执行一次,而且线程号也不一样,说明是异步执行。

@EnableAsync 注解类

还可以通过 @EnableAsync 注解服务类:

代码语言:javascript
复制
@Service
@EnableAsync
public class SchedulerPoolService { 

@Async()
@Scheduled(fixedDelay = 3000)
public void task(){ 

Thread thread =  Thread.currentThread();
try { 

Thread.sleep(5000);
} catch (InterruptedException e) { 

e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
}

调用:

代码语言:javascript
复制
public class TaskSchedulerTest { 

public static void main(String[] args) { 

ApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerDemo.SpringConfig.class);
}
}

运行结果:

一般 @EnableScheduling@EnableAsync 都会结合 @Configuration 使用,用于配置类。 @Configuration @EnableAsync @EnableScheduling public class AppConfig { }

@Async 指定执行器

由上面例子结果看出,@Async 注解默认使用任务执行器 SimpleAsyncTaskExecutor,而此实现每次执行一个提交的任务时候都会新建一个线程,没有线程的复用,一般使用ThreadPoolTaskExecutor 来代替。

当需要指定执行器时,可以使用@Async注解的 value属性。

代码语言:javascript
复制
@Service
public class SchedulerPoolService { 

@Async("taskExecutor")
@Scheduled(fixedDelay = 3000)
public void task(){ 

Thread thread =  Thread.currentThread();
try { 

Thread.sleep(5000);
} catch (InterruptedException e) { 

e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
}
}

applicationContext.xml中注解配置如下:

代码语言:javascript
复制
<bean id = "taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value = "5"></property>
<property name = "maxPoolSize" value="10"></property>
<property name="queueCapacity" value="25"></property>
</bean>

也可以使用 task:executor 配置,下文会讲解。

task命名空间

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

scheduler元素

创建具有指定线程池大小的ThreadPoolTaskScheduler实例。

代码语言:javascript
复制
<task:scheduler id="scheduler" pool-size="10"/>

如果不提供“池大小”属性,默认线程池将只有一个线程。调度程序没有其他配置选项。

executor元素

创建一个ThreadPoolTaskExecutor实例。

代码语言:javascript
复制
<task:executor id="executor" pool-size="10"/>

“executor”元素比“scheduler”元素支持更多的配置选项。首先,ThreadPoolTaskExecutor的线程池本身更具可配置性,pool-size 可以使得执行程序的线程池具有不同的核心值和最大大小,而不是单一大小。

代码语言:javascript
复制
<task:executor id="executorWithPoolSizeRange" pool-size="5-25" queue-capacity="100"/>

id 属性值可以用作指定执行器

代码语言:javascript
复制
@Async("executorWithPoolSizeRange")
public void foo() { 

System.out.println("foo, " + Thread.currentThread().getName());
}

queue-capacity 主要思想是,当提交任务时,如果当前活跃线程的数量小于 core size,执行器将首先尝试使用空闲线程。如果已经达到 core size,那么只要队列的容量未满,任务就会被添加到队列中。 只有在达到queue-capacity时,执行器才会创建一个超出core size的新线程。如果已达到 max size,则执行程序将拒绝该任务。

默认情况下,队列是无限的,但这不是理想的配置,因为如果在所有池线程繁忙时向队列添加了足够的任务,就会导致outofmemoryerror错误。此外,如果队列是无限的,那么max size根本不起作用。因为执行器将总是在线程数超出core size时,将新建的线程加入队列。一个队列必须是有限的。

scheduled-tasks元素

可以通过 scheduled-tasks 配置要调度的任务。

代码语言:javascript
复制
<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"/>

pojo:

代码语言:javascript
复制
@Service
public class beanA{ 

public void methodA() { 

System.out.println(Thread.currentThread().getName() + ":执行");
}
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/183254.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 任务调度接口:TaskScheduler
  • Trigger接口
  • Trigger实现
    • PeriodicTrigger
      • CronTrigger
      • TaskScheduler接口
      • TaskScheduler实现
        • ConcurrentTaskScheduler
          • ThreadPoolTaskScheduler
            • 任务调度Demo
            • 注入方式
            • 注解方式(自动启动)
            • 完全注解开发(自动启动)
          • Spring异步执行
            • @Async 注解方法
            • @EnableAsync 注解类
            • @Async 指定执行器
          • task命名空间
            • scheduler元素
            • executor元素
            • scheduled-tasks元素
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档