介绍三种实现方式:
JDK 自带的定时器实现
- Timer类
这个类允许你调度一个java.util.TimerTask任务。主要有以下几个方法:
void | schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务。 |
void | schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。 |
void | schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务。 |
void | schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。 |
void | scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定速率执行。 |
void | scheduleAtFixedRate(TimerTask task, long delay, long period) 安排指定的任务在指定的延迟后开始进行重复的固定速率执行。 |
ScheduledExecutorService 接口实现类
java.util.concurrent.ScheduledExecutorService
| schedule(Callable<V> callable, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的 ScheduledFuture。 | |
ScheduledFuture<?> | schedule(Runnable command, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的一次性操作。 | |
ScheduledFuture<?> | scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。 | |
ScheduledFuture<?> | scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。 |
默认实现为ScheduledThreadPoolExecutor 继承了ThreadPoolExecutor 的线程池特性,配合future特性,比Timer更强大。
方法摘要 | ||
---|---|---|
protected
| decorateTask(Callable<V> callable, RunnableScheduledFuture<V> task) 修改或替换用于执行 callable 的任务。 | |
protected
| decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) 修改或替换用于执行 runnable 的任务。 | |
void | execute(Runnable command) 使用所要求的零延迟执行命令。 | |
boolean | getContinueExistingPeriodicTasksAfterShutdownPolicy() 获取有关在此执行程序已 shutdown 的情况下、是否继续执行现有定期任务的策略。 | |
boolean | getExecuteExistingDelayedTasksAfterShutdownPolicy() 获取有关在此执行程序已 shutdown 的情况下是否继续执行现有延迟任务的策略。 | |
BlockingQueue<Runnable> | getQueue() 返回此执行程序使用的任务队列。 | |
boolean | remove(Runnable task) 从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。 | |
| schedule(Callable<V> callable, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的 ScheduledFuture。 | |
ScheduledFuture<?> | schedule(Runnable command, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的一次性操作。 | |
ScheduledFuture<?> | scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。 | |
ScheduledFuture<?> | scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。 | |
void | setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) 设置有关在此执行程序已 shutdown 的情况下是否继续执行现有定期任务的策略。 | |
void | setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) 设置有关在此执行程序已 shutdown 的情况下是否继续执行现有延迟任务的策略。 | |
void | shutdown() 在以前已提交任务的执行中发起一个有序的关闭,但是不接受新任务。 | |
List<Runnable> | shutdownNow() 尝试停止所有正在执行的任务、暂停等待任务的处理,并返回等待执行的任务列表。 | |
| submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。 | |
Future<?> | submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 | |
| submit(Runnable task, T result) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 |
Quartz 定时器实现
Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。可以动态的添加删除定时任务,另外很好的支撑集群调度
。简单地创建一个org.quarz.Job接口的Java类,Job接口包含唯一的方法:
public void execute(JobExecutionContext context) throws JobExecutionException;
- 在Job接口实现类里面,添加需要的逻辑到execute()方法中。配置好Job实现类并设定好调度时间表(Trigger),Quartz就会自动在设定的时间调度作业执行execute()。
整合了Quartz的应用程序可以重用不同事件的作业,还可以为一个事件组合多个作业。Quartz通过属性文件来配置JDBC事务的数据源、全局作业、触发器侦听器、插件、线程池等等。(quartz.properties)
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
- 创建Job类
public class TestJob implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
println(Thread.currentThread().getName() + " test job begin " + DateUtil.getCurrentTimeStr());
}
}
2.调度任务
public static void main(String[] args) throws InterruptedException, SchedulerException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
// 开始
scheduler.start();
// job 唯一标识 test.test-1
JobKey jobKey = new JobKey("test" , "test-1");
JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("test" , "test")
// 延迟一秒执行
.startAt(new Date(System.currentTimeMillis() + 1000))
// 每隔一秒执行 并一直重复
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
scheduler.scheduleJob(jobDetail , trigger);
Thread.sleep(5000);
// 删除job
scheduler.deleteJob(jobKey);
}
out :
DefaultQuartzScheduler_Worker-1test job begin 2017-06-03 14:30:33
DefaultQuartzScheduler_Worker-2test job begin 2017-06-03 14:30:34
DefaultQuartzScheduler_Worker-3test job begin 2017-06-03 14:30:35
DefaultQuartzScheduler_Worker-4test job begin 2017-06-03 14:30:36
DefaultQuartzScheduler_Worker-5test job begin 2017-06-03 14:30:37
Quartz 主要包含以下几个部分
Job:是一个接口,只有一个方法void execute(JobExecutionContext
context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;
Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。
Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler#
getContext()获取对应的SchedulerContext实例;
ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
关于简单使用,可以参考quartz的example,下面链接是一些入门帮助。
Quartz定时任务学习(一)简单任务
Quartz定时任务学习(二)web应用
Quartz定时任务学习(三)属性文件和jar
深入学习可以阅读官方文档和相关博客阅读
以下为推荐博客地址
quartz详解2:quartz由浅入深
Spring 相关的任务调度
- Spring 3.0+ 自带的任务调度实现,主要依靠
TaskScheduler
接口的几个实现类实现。删除和修改任务比较麻烦。
主要用法有以下三种:- Spring配置文件实现
- 注解实现
- 代码动态添加
配置文件实现
spring-schedule.xml
<task:scheduler id="myScheduler" pool-size="10" />
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="job" method="test" cron="0 * * * * ?"/>
</task:scheduled-tasks>
注解实现
spring-schedule.xml
<task:scheduler id="myScheduler" pool-size="10" />
// 启用注解
<task:annotation-driven scheduler="myScheduler"/>
@Component
public class Task{
@Scheduled(cron="0/5 * * * * ? ") //每5秒执行一次
public void execute(){
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(DateTime.now().toDate())+"*********B任务每5秒执行一次进入测试");
}
}
代码动态添加
spring-schedule.xml
<bean id = "myScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="10"/>
<property name="threadGroupName" value="myScheduler" />
<property name="threadNamePrefix" value="-1" />
</bean>
<task:annotation-driven scheduler="myScheduler"/>
@Component
public class Test {
@Autowired
private ThreadPoolTaskScheduler myScheduler;
public void addJob(){
myScheduler.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
}
} , new CronTrigger("0/5 * * * * ? ")); //每5秒执行一次
}
}
spring 结合 quartz 实现任务调度
- spring 配置文件 spring-quartz.xml
<bean id="quartzsScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="triggers">
<list>
<ref bean="testTrigger" />
</list>
</property>
</bean>
<!-- jobClass需要继承QuartzJobBean 也可以使用 MethodInvokingJobDetailFactoryBean 定义任意类任意方法为Job-->
<bean id="testJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass">
<value>com.test.TestJob</value>
</property>
<property name="durability" value="true" />
<!-- requestsRecovery属性必须设置为 true,当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务 -->
<property name="requestsRecovery" value="true" />
</bean>
<bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="testJobDetail" />
<property name="cronExpression" value="0 0 10 * * ?" />
</bean>
动态增加删除
@Component
public class Test {
@Autowired
private SchedulerFactoryBean quartzScheduler;
public void addJob() throws SchedulerException {
Scheduler scheduler = quartzScheduler.getScheduler();
JobKey jobKey = new JobKey("test", "test");
if (scheduler.checkExists(jobKey)) {
return;
}
JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("test", "test")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();
scheduler.scheduleJob(jobDetail, trigger);
}
}