-
- 任務調度
定時任務調度:基於給定的時間點、給定的時間間隔、給定的執行次數自動執行的任務。
-
-
- Timer
-
介紹
Timer,簡單無門檻,一般也沒人用。
Timer位於java.util包下,其內部包含且僅包含一個后台線程(TimeThread)對多個業務任務(TimeTask)進行定時定頻率的調度。
參數說明:
task:所要執行的任務,需要extends TimeTask override run()
time/firstTime:首次執行任務的時間
period:周期性執行Task的時間間隔,單位是毫秒
delay:執行task任務前的延時時間,單位是毫秒
很顯然,通過上述的描述,我們可以實現:
延遲多久后執行一次任務;指定時間執行一次任務;延遲一段時間,並周期性執行任務;指定時間,並周期性執行任務;
注意點
1:如果time/firstTime指定的時間,在當前時間之前,會發生什么呢?
在時間等於或者超過time/firstTime的時候,會執行task!也就是說,如果time/firstTime指定的時間在當前時間之前,就會立即得到執行。
2:schedule和scheduleAtFixedRate有什么區別?
scheduleAtFixedRate:每次執行時間為上一次任務開始起向后推一個period間隔,也就是說下次執行時間相對於上一次任務開始的時間點,因此執行時間不會延后,但是存在任務並發執行的問題。
schedule:每次執行時間為上一次任務結束后推一個period間隔,也就是說下次執行時間相對於上一次任務結束的時間點,因此執行時間會不斷延后。
3:如果執行task發生異常,是否會影響其他task的定時調度?
如果TimeTask拋出RuntimeException,那么Timer會停止所有任務的運行!
4:Timer的一些缺陷?
前面已經提及到Timer背后是一個單線程,因此Timer存在管理並發任務的缺陷:所有任務都是由同一個線程來調度,所有任務都是串行執行,意味着同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之后的任務。
其次,Timer的一些調度方式還算比較簡單,無法適應實際項目中任務定時調度的復雜度。
5:停止和移除
cancel():終止Timer計時器,丟棄所有當前已安排的任務(TimeTask也存在cancel()方法,不過終止的是TimeTask)
purge():從計時器的任務隊列中移除已取消的任務,並返回個數
代碼示例
package schedule.timer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
/**
* Timer背后是一個單線程,因此Timer存在管理並發任務的缺陷:所有任務都是由同一個線程來調度,所有任務都是串行執行,意味着同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之后的任務。
* 其次,Timer的一些調度方式還算比較簡單,無法適應實際項目中任務定時調度的復雜度。
*
*
*/
public class TimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTask(), Calendar.getInstance().getTime(), 1000);
}
}
class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("start at :"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(this.scheduledExecutionTime()));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-
-
- ScheduledExecutorService
-
介紹
JDK5之后便提供了基於線程池的定時任務調度:ScheduledExecutorService。
設計理念:每一個被調度的任務都會被線程池中的一個線程去執行,因此任務可以並發執行,而且相互之間不受影響。
示例
package schedule.scheduledExecutor;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
scheduledExecutorService.scheduleAtFixedRate(new ExecutorDemo(), 1000, 2000, TimeUnit.MILLISECONDS);
}
}
class ExecutorDemo implements Runnable{
@Override
public void run() {
System.out.println("執行"+new Date());
}
}
-
-
- spring task
-
spring @Scheduled注解,一般集成於項目中,小任務很方便。
fixedRate和fixedDelay的區別
fixedRate :每隔多少毫秒執行一次該方法, 以上一次執行開始時間計算
fixedDelay:當一次方法執行完畢之后,延遲多少毫秒再執行該方法
例子1 注解方式
package schedule.springTask1;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTaskAnnotationTest {
public static void main(String[] args) throws BeansException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask1/spring-mvc.xml");
}
}
package schedule.springTask1;
import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 基於注解的定時器
*/
@Component
public class SpringTaskAnnotation {
// 定時計算。每一秒執行一次
@Scheduled(cron = "0/1 * * * * *")
public void show() {
System.out.println(new Date() + " : Annotation:is show run");
}
/**
* 心跳更新。啟動時執行一次,之后每隔2秒執行一次
*/
@Scheduled(fixedRate = 1000 * 2)
public void print() {
System.out.println(new Date() + " : Annotation:is print run");
}
// 啟動加載緩存, 以上一次執行完為准
@Scheduled(fixedDelay = 1 * 1000)
public void init() {
System.out.println(new Date() + ": Annotation:is init run");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- 定時器注解開關, 可以配置 scheduler參數 -->
<task:annotation-driven />
<!-- bean注解開關 -->
<context:annotation-config />
<!-- 自動掃描的包名 -->
<context:component-scan base-package="schedule.springTask1" />
</beans>
例子2 xml方式
package schedule.springTask2;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTaskXmlTest {
public static void main(String[] args) throws BeansException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask2/spring-mvc.xml");
}
}
package schedule.springTask2;
import java.util.Date;
/**
* 基於xml的定時器
*/
public class SpringTaskXml {
public void show() {
System.out.println(new Date() + " : XMl is show run");
}
public void print() {
System.out.println(new Date() + " : XMl print run");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<bean id="task" class="schedule.springTask2.SpringTaskXml"></bean>
<task:scheduled-tasks>
<!-- 這里表示的是每隔五秒執行一次 -->
<task:scheduled ref="task" method="show"
cron="*/5 * * * * ?" />
<task:scheduled ref="task" method="print"
cron="*/10 * * * * ?" />
</task:scheduled-tasks>
</beans>
-
-
- Spring Quartz
-
開源工具 Quartz,分布式集群開源工具,可以說是中小型公司必選,當然也視自身需求而定。
介紹
兩種方式實現Spirng定時任務:
1.繼承自org.springframework.scheduling.quartz.QuartzJobBean
2.作業類即普通的java類,不需要繼承自任何基類。
定時器任務有兩種觸發器:
1.按照一定頻度調用任務,在Spring Quartz中對應的觸發器為:org.springframework.scheduling.quartz.SimpleTriggerBean
2.按照指定時間調用任務,在Spring Quartz中對應的調度器為:org.springframework.scheduling.quartz.CronTriggerBean
說明:
1、從代碼上來看,有XxxBuilder、XxxFactory,說明Quartz用到了Builder、Factory模式,還有非常易懂的鏈式編程風格。
2、Quartz有3個核心概念:調度器(Scheduler)、任務(Job&JobDetail)、觸發器(Trigger)。(一個任務可以被多個觸發器觸發,一個觸發器只能觸發一個任務)
3、注意當Scheduler調度Job時,實際上會通過反射newInstance一個新的Job實例(待調度完畢后銷毀掉),同時會把JobExecutionContext傳遞給Job的execute方法,Job實例通過JobExecutionContext訪問到Quartz運行時的環境以及Job本身的明細數據。
4、JobDataMap可以裝載任何可以序列化的數據,存取很方便。需要注意的是JobDetail和Trigger都可以各自關聯上JobDataMap。JobDataMap除了可以通過上述代碼獲取外,還可以在YourJob實現類中,添加相應setter方法獲取。
5、Trigger用來告訴Quartz調度程序什么時候執行,常用的觸發器有2種:SimpleTrigger(類似於Timer)、CronTrigger(類似於Linux的Crontab)。
6、實際上,Quartz在進行調度器初始化的時候,會加載quartz.properties文件進行一些屬性的設置,比如Quartz后台線程池的屬性(threadCount)、作業存儲設置等。它會先從工程中找,如果找不到那么就是用quartz.jar中的默認的quartz.properties文件。
7、Quartz存在監聽器的概念,比如任務執行前后、任務的添加等,可以方便實現任務的監控。
cronExpression配置說明
字段 |
允許值 |
允許的特殊字符 |
秒 |
0-59 |
, - * / |
分 |
0-59 |
, - * / |
小時 |
0-23 |
, - * / |
日期 |
1月31日 |
, - * ? / L W C |
月份 |
1-12 或者 JAN-DEC |
, - * / |
星期 |
1-7 或者 SUN-SAT |
, - * ? / L C # |
年(可選) |
留空, 1970-2099 |
, - * / |
特殊字符說明:
字符 |
意義 |
* |
表示所有值; |
? |
表示未說明的值,即不關心它為何值; |
- |
表示一個指定的范圍; |
, |
表示附加一個可能值; |
/ |
符號前表示開始時間,符號后表示每次遞增的值; |
L(“last”) |
(“last”) “L” 用在day-of-month字段意思是 |
W(“weekday”) |
只能用在day-of-month字段。用來描敘最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近這個月第15天的工作日”,即如果這個月第15天是周六,那么觸發器將會在這個月第14天即周五觸發;如果這個月第15天是周日,那么觸發器將會在這個月第16 天即周一觸發;如果這個月第15天是周二,那么就在觸發器這天觸發。注意一點:這個用法只會在當前月計算值,不會越過當前月。“W”字符僅能在day- of-month指明一天,不能是一個范圍或列表。也可以用“LW”來指定這個月的最后一個工作日。 |
# |
只能用在day-of-week字段。用來指定這個月的第幾個周幾。例:在day-of-week字段用”6#3”指這個月第3個周五(6指周五,3指第3個)。如果指定的日期不存在,觸發器就不會觸發。 |
C |
指和calendar聯系后計算過的值。例:在day-of-month 字段用“5C”指在這個月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在這周日或之后包括calendar的第一天。 |
Cron表達式教程
這里給出一些常用的示例:
0 15 10 ? * 每天10點15分觸發
0 15 10 ? 2017 2017年每天10點15分觸發
0 14 * ? 每天下午的 2點到2點59分每分觸發
0 0/5 14 ? 每天下午的 2點到2點59分(整點開始,每隔5分觸發)
0 0/5 14,18 ? 每天下午的 2點到2點59分、18點到18點59分(整點開始,每隔5分觸發)
0 0-5 14 ? 每天下午的 2點到2點05分每分觸發
0 15 10 ? * 6L 每月最后一周的星期五的10點15分觸發
0 15 10 ? * 6#3 每月的第三周的星期五開始觸發
我們可以通過一些Cron在線工具非常方便的生成,比如http://www.pppet.net/等。
CronTrigger
CronTriggers往往比SimpleTrigger更有用,如果您需要基於日歷的概念,而非SimpleTrigger完全指定的時間間隔,復發的發射工作的時間表。 CronTrigger,你可以指定觸發的時間表如“每星期五中午”,或“每個工作日9:30時”,甚至“每5分鍾一班9:00和10:00逢星期一上午,星期三星期五“。 即便如此,SimpleTrigger一樣,CronTrigger擁有的startTime指定的時間表時生效,指定的時間表時,應停止(可選)結束時間。
Cron表達式
cron的表達式被用來配置CronTrigger實例。 cron的表達式是字符串,實際上是由七子表達式,描述個別細節的時間表。這些子表達式是分開的空白,代表:
1. Seconds
2. Minutes
3. Hours
4. Day-of-Month
5. Month
6. Day-of-Week
7. Year (可選字段)
例 "0 0 12 ? * WED" 在每星期三下午12:00 執行,
個別子表達式可以包含范圍, 例如,在前面的例子里("WED")可以替換成 "MON-FRI", "MON, WED, FRI"甚至"MON-WED,SAT". “*” 代表整個時間段.
每一個字段都有一套可以指定有效值,如
Seconds (秒) :可以用數字0-59 表示,
Minutes(分) :可以用數字0-59 表示,
Hours(時) :可以用數字0-23表示,
Day-of-Month(天) :可以用數字1-31 中的任一一個值,但要注意一些特別的月份
Month(月) :可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示
Day-of-Week(每周):可以用數字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示
“/”:為特別單位,表示為“每”如“0/15”表示每隔15分鍾執行一次,“0”表示為從“0”分開始, “3/20”表示表示每隔20分鍾執行一次,“3”表示從第3分鍾開始執行
“?”:表示每月的某一天,或第周的某一天
“L”:用於每月,或每周,表示為每月的最后一天,或每個月的最后星期幾如“6L”表示“每月的最后一個星期五”
“W”:表示為最近工作日,如“15W”放在每月(day-of-month)字段上表示為“到本月15日最近的工作日”
““#”:是用來指定“的”每月第n個工作日,例 在每周(day-of-week)這個字段中內容為"6#3" or "FRI#3" 則表示“每月第三個星期五”
例子 1 cron簡單和復雜例子
package schedule.quartz1;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTaskAnnotationTest {
public static void main(String[] args) throws BeansException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/quartz1/spring-mvc.xml");
}
}
package schedule.quartz1;
public class SimpleJob {
protected void execute() {
System.out.println("簡單定時任務執行中…");
}
}
package schedule.quartz1;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class ComplexJob extends QuartzJobBean {
private int timeout;
// 調度工廠實例化后,經過timeout時間開始執行調度
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* 要調度的具體任務
*/
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("復雜定時任務執行中…");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<bean id="simpleJob" class="schedule.quartz1.SimpleJob"></bean>
<!-- 使用 MethodInvokingJobDetailFactoryBean, 指定特定方法 -->
<bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="simpleJob" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" /><!-- 作業不並發調度 -->
</bean>
<!-- 簡單觸發器,使用 SimpleTriggerFactoryBean -->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="simpleJobDetail" />
<property name="startDelay" value="1000" /><!-- 調度工廠實例化后,經過1000秒開始執行調度 -->
<property name="repeatInterval" value="2000" /><!-- 每2秒調度一次 -->
</bean>
<!-- 高級設置, 給作業傳遞數據 -->
<bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="schedule.quartz1.ComplexJob" />
<property name="jobDataMap">
<map>
<entry key="timeout" value="1" />
</map>
</property>
<property name="durability" value="true" />
</bean>
<!-- 計划觸發器,使用 CronTriggerFactoryBean -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="complexJobDetail" />
<!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->
<property name="cronExpression" value="0/5 * * ? * *" />
</bean>
<!-- 調度器 -->
<bean
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
<ref bean="cronTrigger" />
</list>
</property>
</bean>
</beans>
例子 2 Scheduler start/stop manager配置方式
package schedule.quartz2;
import org.quartz.SchedulerException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTaskAnnotationTest {
public static void main(String[] args) throws BeansException, InterruptedException, SchedulerException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/schedule/quartz2/spring-mvc.xml");
QuartzManager quartzManager = applicationContext.getBean(QuartzManager.class);
quartzManager.initJobs();
quartzManager.addJob("xxxx", "yyyy", "zzzz", "gggg",MyJob.class, "0/3 * * ? * *");
Thread.sleep(2000);
System.out.println("--change to 2s--");
quartzManager.trigger("xxxxcc", "yyyy", "zzzzx", "ggggx",MyJob2.class, "0/1 * * ? * *");
quartzManager.modifyJobTime("xxxx", "yyyy", "zzzz", "gggg", "0/2 * * ? * *");
// Thread.sleep(20000);
System.out.println("--stop--");
// quartzManager.shutdownJobs();
Thread.sleep(20000);
}
}
package schedule.quartz2;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
public class QuartzManager {
private Scheduler scheduler;
@Autowired
private IJobService jobService;
//初始化所有的jobs
public void initJobs() {
Long count = jobService.listQuartzEntity(null);
if(0!=count) {
System.out.println(".....");
}
}
/**
* @Description: 添加一個定時任務
*
* @param jobName 任務名
* @param jobGroupName 任務組名
* @param triggerName 觸發器名
* @param triggerGroupName 觸發器組名
* @param jobClass 任務
* @param cron 時間設置,參考quartz說明文檔
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) {
try {
// 任務名,任務組,任務執行類
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
// 觸發器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 觸發器名,觸發器組
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 觸發器時間設定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 創建Trigger對象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
// 調度容器設置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 啟動
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 觸發任務
* @throws SchedulerException
*/
public void trigger(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {
JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//沒有對應的job和trigger
if(null==jobDetail && null==scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {
addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
}else {
JobKey key = new JobKey(jobName, jobGroupName);
scheduler.triggerJob(key);
}
}
/**
* 停止任務
* @throws SchedulerException
*/
public void pauseJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {
JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//沒有對應的job和trigger
if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {
JobKey key = new JobKey(jobName, jobGroupName);
scheduler.pauseJob(key);
}
}
/**
* 恢復任務
* @throws SchedulerException
*/
public void resumeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {
JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//沒有對應的job和trigger
if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {
JobKey key = new JobKey(jobName, jobGroupName);
scheduler.resumeJob(key);
}
}
/**
* @Description: 修改一個任務的觸發時間
*
* @param jobName
* @param jobGroupName
* @param triggerName 觸發器名
* @param triggerGroupName 觸發器組名
* @param cron 時間設置,參考quartz說明文檔
*/
public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(cron)) {
/** 方式一 :調用 rescheduleJob 開始 */
// 觸發器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 觸發器名,觸發器組
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 觸發器時間設定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 創建Trigger對象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一個任務的觸發時間
scheduler.rescheduleJob(triggerKey, trigger);
/** 方式一 :調用 rescheduleJob 結束 */
/** 方式二:先刪除,然后在創建一個新的Job */
// JobDetail jobDetail =
// scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
// Class<? extends Job> jobClass = jobDetail.getJobClass();
// removeJob(jobName, jobGroupName, triggerName,
// triggerGroupName);
// addJob(jobName, jobGroupName, triggerName, triggerGroupName,
// jobClass, cron);
/** 方式二 :先刪除,然后在創建一個新的Job */
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一個任務
*
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*/
public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
scheduler.pauseTrigger(triggerKey);// 停止觸發器
scheduler.unscheduleJob(triggerKey);// 移除觸發器
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 刪除任務
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:啟動所有定時任務
*/
public void startJobs() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:關閉所有定時任務
*/
public void shutdownJobs() {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Scheduler getScheduler() {
return scheduler;
}
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
public String transCron(String time) {
String seconds = StringUtils.substringAfterLast(time, ":");
String minute = StringUtils.substringAfter(time, ":").substring(0, 2);
String hour = StringUtils.substringAfter(time, " ").substring(0, 2);
String day = StringUtils.substringAfterLast(time, "-").substring(0, 2);
String month = StringUtils.substringAfter(time, "-").substring(0, 2);
return seconds + " " + minute + " " + hour + " " + day + " " + month + " ?";
}
}
package schedule.quartz2;
import lombok.Data;
@Data
public class QuartzEntity{
private String jobName;//任務名稱
private String jobGroup;//任務分組
private String description;//任務描述
private String jobClassName;//執行類
private String cronExpression;//執行時間
private String triggerName;//執行時間
private String triggerState;//任務狀態
private String oldJobName;//任務名稱 用於修改
private String oldJobGroup;//任務分組 用於修改
}
package schedule.quartz2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyJob implements Job {
private Logger logger = LoggerFactory.getLogger(MyJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
logger.info("【test】動態定時調度測試");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package schedule.quartz2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyJob2 implements Job {
private Logger logger = LoggerFactory.getLogger(MyJob2.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
logger.info("【test】動態定時調度測試 ------------2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package schedule.quartz2;
import java.util.List;
public interface IJobService {
List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,Integer pageNo,Integer pageSize);
Long listQuartzEntity(QuartzEntity quartz);
}
package schedule.quartz2;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service("jobService")
public class JobServiceImpl implements IJobService {
@Autowired
private DynamicQuery dynamicQuery;
@Override
public List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,
Integer pageNo, Integer pageSize) {
StringBuffer nativeSql = new StringBuffer();
nativeSql.append("SELECT job.JOB_NAME as jobName,job.JOB_GROUP as jobGroup,job.DESCRIPTION as description,job.JOB_CLASS_NAME as jobClassName,");
nativeSql.append("cron.CRON_EXPRESSION as cronExpression,tri.TRIGGER_NAME as triggerName,tri.TRIGGER_STATE as triggerState,");
nativeSql.append("job.JOB_NAME as oldJobName,job.JOB_GROUP as oldJobGroup ");
nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");
nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");
nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");
Object[] params = new Object[]{};
if(!StringUtils.isEmpty(quartz.getJobName())){//加入JobName搜索其他條件自行實現
nativeSql.append(" AND job.JOB_NAME = ?");
params = new Object[]{quartz.getJobName()};
}
return null;
}
@Override
public Long listQuartzEntity(QuartzEntity quartz) {
return 0L;
// StringBuffer nativeSql = new StringBuffer();
// nativeSql.append("SELECT COUNT(*)");
// nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");
// nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");
// nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");
// return dynamicQuery.nativeQueryCount(nativeSql.toString(), new Object[]{});
}
}
package schedule.quartz2;
import java.util.List;
/**
* 擴展SpringDataJpa, 支持動態jpql/nativesql查詢並支持分頁查詢
* 使用方法:注入ServiceImpl
*/
public interface DynamicQuery {
}
package schedule.quartz2;
import org.springframework.stereotype.Repository;
@Repository
public class DynamicQueryImpl implements DynamicQuery {
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:component-scan base-package="schedule.quartz2"></context:component-scan>
<bean id="startQuartz" lazy-init="true" autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!--
<ref bean="cronTrigger" />
-->
</list>
</property>
</bean>
<bean id="quartzManager" class="schedule.quartz2.QuartzManager" lazy-init="false" init-method="startJobs">
<!--這個對象一定要注入,這樣類才能進行管理,還有在類型要用get set方法,不然會報錯。 -->
<property name="scheduler" ref="startQuartz" />
</bean>
<!-- 高級設置, 給作業傳遞數據 -->
<bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="schedule.quartz2.MyJob" />
<property name="jobDataMap">
<map>
<entry key="timeout" value="1" />
</map>
</property>
<property name="durability" value="true" />
</bean>
<!-- 計划觸發器,使用 CronTriggerFactoryBean -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="complexJobDetail" />
<!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->
<property name="cronExpression" value="0/5 * * ? * *" />
</bean>
</beans>
例子 3 Scheduler start/stop java方式
package schedule.quartz3;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args) throws Exception {
// 從調度程序工廠獲取一個調度程序的實例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 顯示調度程序的名稱(這里會展示我們在quartz.properties文件中的名稱)
System.out.println("scheduleName = " + scheduler.getSchedulerName());
/**
* 重要: 定義一個job,並綁定到我們自定義的HelloJob的class對象
* 這里並不會馬上創建一個HelloJob實例,實例創建是在scheduler安排任務觸發執行時創建的 這種機制也為后面使用Spring集成提供了便利
*/
JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
// 聲明一個觸發器,現在就執行(schedule.start()方法開始調用的時候執行);並且每間隔2秒就執行一次
// 觸發器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 觸發器名,觸發器組
triggerBuilder.withIdentity("trigger1", "group1");
triggerBuilder.startNow();
// 觸發器時間設定
// triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * ? * *"));
triggerBuilder.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever());
Trigger trigger = triggerBuilder.build();
// 告訴quartz使用定義的觸發器trigger安排執行任務job
scheduler.scheduleJob(job, trigger);
// 啟動任務調度程序,內部機制是線程的啟動
scheduler.start();
// 關閉任務調度程序,如果不關閉,調度程序schedule會一直運行着
// scheduler.shutdown();
}
}
package schedule.quartz3;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyJob implements Job {
private Logger logger = LoggerFactory.getLogger(MyJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
logger.info("【test】動態定時調度測試");
} catch (Exception e) {
e.printStackTrace();
}
}
}
-
-
- XXL-JOB
-
分布式任務 XXL-JOB,是一個輕量級分布式任務調度框架,支持通過 Web 頁面對任務進行 CRUD 操作,支持動態修改任務狀態、暫停/恢復任務,以及終止運行中任務,支持在線配置調度任務入參和在線查看調度結果。
https://github.com/xuxueli/xxl-job
https://www.cnblogs.com/xuxueli/p/5021979.html
-
-
- Elastic-Job
-
分布式任務 Elastic-Job,是一個分布式調度解決方案,由兩個相互獨立的子項目 Elastic-Job-Lite 和 Elastic-Job-Cloud 組成。定位為輕量級無中心化解決方案,使用 jar 包的形式提供分布式任務的協調服務。支持分布式調度協調、彈性擴容縮容、失效轉移、錯過執行作業重觸發、並行調度、自診。
https://gitee.com/52itstyle/spring-boot-quartz