Java開發過程中經常會遇到使用定時任務的情況,我總結了一下有如下四種方式:Timer、ScheduledExecutorService、SpringTask、Quartz。
一、使用java的Timer
1、Timer
new Timer("testTimer").schedule(new TimerTask() {
@Override
public void run() {
System.out.println("TimerTask");
}
}, 1000,2000);
解釋:1000ms是延遲啟動時間,2000ms是定時任務周期,每2s執行一次
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
Date date = dateFormat.parse("2018-07-11 12:00:00.000");
new Timer("testTimer1").scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("TimerTask");
}
}, date,2000);
} catch (ParseException e) {
e.printStackTrace();
}
解釋:date是開始時間,2000ms是定時任務周期,每2s執行一次
timer有2中方法schedule和scheduleAtFixedRate,前者會等任務結束在開始計算時間間隔,后者是在任務開始就計算時間,有並發的情況
二、使用ScheduledExecutorService
1、ScheduledExecutorService
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("ScheduledTask");
}
},1, TimeUnit.SECONDS);
解釋:延遲1s啟動,執行一次
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("ScheduledTask");
}
}, 1, 1, TimeUnit.SECONDS);
解釋:延遲1s啟動,每隔1s執行一次,是前一個任務開始時就開始計算時間間隔,但是會等上一個任務結束在開始下一個
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("ScheduledTask");
}
}, 1, 1, TimeUnit.SECONDS);
解釋:延遲1s啟動,在前一個任務執行完成之后,延遲1s在執行
三、使用SpringTask
1、寫任務類
package com.zb.timedtask;
import com.zb.controller.StudentController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class SpringTask {
private static final Logger log = LoggerFactory.getLogger(SpringTask.class);
@Scheduled(cron = "1/5 * * * * *")
public void task1(){
log.info("springtask 定時任務!");
}
@Scheduled(initialDelay = 1000,fixedRate = 1*1000)
public void task2(){
log.info("springtask 定時任務!");
}
}
解釋:
task1是每隔5s執行一次,{秒} {分} {時} {日期} {月} {星期}
task2是延遲1s,每隔1S執行一次
2、配置文件修改
(1)簡單版
<task:annotation-driven/>
(2)任務池版
<task:executor id="executor" pool-size="10" />
<task:scheduler id="scheduler" pool-size="10" />
<task:annotation-driven executor="executor" scheduler="scheduler" />
(3)解釋
假如只有一個定時任務,可以用簡單版;如果有多個定時任務,則要用任務池,不然它會順序執行。
兩個任務的時間間隔為:執行時間+設定的定時間隔
例子:(這個任務8s執行一次)
@Scheduled(cron = "1/4 * * * * *")
public void task2(){
log.info("springtask 定時任務2!");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
四、使用Quartz框架
1、加依賴
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!--調度器核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
2、Job實現
package com.zb.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HelloWorldJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
System.out.println( strTime + ":Hello World!");
}
}
3、調度器(可以用listener在項目啟動時執行)
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class MyScheduler {
public static void main(String[] args) throws SchedulerException {
//創建調度器Schedule
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//創建JobDetail實例,並與HelloWordlJob類綁定
JobDetail jobDetail = JobBuilder.newJob(HelloWorldJob.class).withIdentity("job1", "jobGroup1")
.build();
//創建觸發器Trigger實例(立即執行,每隔1S執行一次)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "triggerGroup1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
//開始執行
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
解釋:上面用的是簡單觸發器,也可以用Con觸發器,如下
Trigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "triggerGroup2")
.startNow()
.withSchedule(cronSchedule("0 42 10 * * ?"))
.build();
4、整合spring
也可以直接把上面的調度器寫成配置文件,整合spring
(1)job
package com.zb.quartz;
import java.text.SimpleDateFormat;
import java.util.Date;
public class QuarFirstJob {
public void first() {
String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
System.out.println( strTime + ":Hello World!");
}
}
(2)配置文件
<bean id="QuarFirstJob" class="com.zb.quartz.QuarFirstJob" />
<bean id="jobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="group" value="quartzGroup1" />
<property name="name" value="quartzJob1" />
<!--false表示等上一個任務執行完后再開啟新的任務 -->
<property name="concurrent" value="false" />
<property name="targetObject">
<ref bean="QuarFirstJob" />
</property>
<property name="targetMethod">
<value>first</value>
</property>
</bean>
<!-- 調度觸發器 -->
<bean id="myTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="trigger1" />
<property name="group" value="group1" />
<property name="jobDetail">
<ref bean="jobDetail" />
</property>
<property name="cronExpression">
<value>0/5 * * * * ?</value>
</property>
</bean>
<!-- 調度工廠 -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="myTrigger" />
</list>
</property>
</bean>
5、時間
public class QuarFirstJob {
public void first() {
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
String strTime = new SimpleDateFormat("HH-mm-ss").format(new Date());
System.out.println( strTime + ":Hello World!");
}
}
上面的配置里面寫是5s間隔,把上面的sleep時間分別改成4和6,發現兩次任務間隔是執行時間和間隔時間的最大值,分別是5,6
總結
Quartz是執行時間和間隔時間的最大值(比如;執行時間是3s,間隔是2s,則每3s執行一次;執行時間是3s,間隔是5s,則每5s執行一次。)
Spring task是執行時間+間隔時間(比如;執行時間是3s,間隔是2s,則每5s執行一次;執行時間是3s,間隔是5s,則每8s執行一次。)
timer有2中方法schedule和scheduleAtFixedRate,前者會等任務結束在開始計算時間間隔,后者是在任務開始就計算時間,有並發的情況
ScheduledExecutorService的scheduleAtFixedRate類似Quartz,scheduleWithFixedDelay類似SpringTask