java週期排程幾種實現
寫程式碼的時候有些任務需要做週期處理,例如每天9點解析檔案並落地到資料庫。這時候就需要選擇合適的任務排程策略。以下是java裡常用的幾個任務排程方法:
1、java.util.Timer, java.util.TimerTask
timer啟動一個非同步執行緒執行任務,週期執行任務策略對應方法scheduleAtFixedRate(TimerTask task, long delay, long period),scheduleAtFixedRate(TimerTask task, Date firstTime, long period)可以指定首次任務時間和週期間隔時間。停止任務執行使用cancel()。
Timer執行任務是單執行緒的,內部用任務佇列來維護待執行任務,任務使用最小堆演算法排序(任務下次執行時間距今越小越優先被執行),新增任務時使用鎖機制防止併發問題。但是如果自定義的任務處理邏輯包含訪問競爭資源,則需要自己進行相關競爭資源的防併發處理。
final AtomicInteger taskExecuteCnt = new AtomicInteger();
TimerTask timerTaskA = new TimerTask() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
timer.cancel();
}
System.out.printf("Task A execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
TimerTask timerTaskB = new TimerTask() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
timer.cancel();
}
System.out.printf("Task B execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
timer.scheduleAtFixedRate(timerTaskA, 0, 500);
timer.scheduleAtFixedRate(timerTaskB, 0, 500);
執行後結果:
Task A execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:24 CST 2015
如上,執行順序為A,B,B,A,A,B,B,A,A,B,B。出現這種同一個任務連續執行是因為Timer內部用佇列維護待處理任務,佇列排序演算法為最小二叉堆(任務下次執行時間距今越小越優先被執行),上述A,B任務的起始執行時間都為0,週期間隔都為500ms。那麼待執行任務佇列堆如下
A
B A1
B1
A執行完後,經過重排堆變為
B
B1 A1
B執行完後,經過重排堆變為
B1
A1
所以就會出現堆重排序導致優先順序相等的任務執行順序不固定,如果需要多個任務以特定順序執行,可通過指定任務首次執行延遲時間來實現,
timer.scheduleAtFixedRate(timerTaskA, 0, 500);
timer.scheduleAtFixedRate(timerTaskB, 1, 500);
如上指定,則同一個週期內A任務永遠先與B任務執行。
另外,如果某個任務執行時間過長,導致下次觸發任務執行時佇列中多個任務下次執行時間已到,則會發生多個任務連續執行。如果任務執行時間對執行結果有影響,則合理指定任務週期,單任務處理的資料量,或者使用非同步處理機制來確保任務執行正確性(任務觸發時只記錄一條任務流水,流水記錄中包括任務觸發時的狀態,後續通過額外的恢復任務處理任務流水,非同步處理邏輯還可以保證在系統故障時不會丟失佇列中待處理的任務)。
示例程式碼如下:
final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
final AtomicInteger taskExecuteCnt = new AtomicInteger();
Runnable runnableA = new Runnable() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
scheduledExecutorService.shutdownNow();
}
System.out.printf("Task A execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
Runnable runnableB = new Runnable() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
scheduledExecutorService.shutdownNow();
}
System.out.printf("Task B execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
scheduledExecutorService.scheduleWithFixedDelay(runnableA, 0, 500, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleWithFixedDelay(runnableB, 0, 500,TimeUnit.MILLISECONDS);
這裡使用的排程方法是scheduleWithFixedDelay,和scheduleWithFixedRate的區別是:
scheduleWithFixedDelay任務的下次執行時間是根據前次執行結束時間和時間間隔計算得到。
scheduleWithFixedRate任務的下次執行時間是根據前次執行開始時間加時間間隔得到。
使用兩個方法可能帶來的影響是,如果任務執行時間過長,scheduleWithFixedDelay計算得到的待執行任務個數小於scheduleWithFixedRate計算得到的任務個數。
3、Quartz
Quzrtz支援用cron表示式來定義靈活多變的任務觸發時間,例如表示式(0/1 * * * * ?)表示每秒觸發任務執行。
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="cronTaskA" class="com.test.CronTask">
<property name="jobContent" value="Task A execute" />
</bean>
<bean id="cronTaskB" class="com.test.CronTask">
<property name="jobContent" value="Task B execute" />
</bean>
<bean id="jobDetailA"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cronTaskA" />
<property name="targetMethod" value="run" />
<property name="concurrent" value="false" />
</bean>
<bean id="jobDetailB"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cronTaskB" />
<property name="targetMethod" value="run" />
<property name="concurrent" value="false" />
</bean>
<bean id="cronTriggerA" class="org.springframework.scheduling.quartz.CronTriggerBean">
<!-- jobDetail -->
<property name="jobDetail" ref="jobDetailA" />
<!-- execute per second -->
<property name="cronExpression" value="0/1 * * * * ?" />
</bean>
<bean id="cronTriggerB" class="org.springframework.scheduling.quartz.CronTriggerBean">
<!-- jobDetail -->
<property name="jobDetail" ref="jobDetailB" />
<!-- execute per second -->
<property name="cronExpression" value="0/1 * * * * ?" />
</bean>
<bean id="schedulerFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerA" />
<ref bean="cronTriggerB" />
</list>
</property>
</bean>
</beans>
自定義任務類實現:
public class CronTask {
/** logger */
private static final Logger logger = LoggerFactory.getLogger(CronTask.class);
/** job content */
private String jobContent;
/**
* task content
*/
public void run() {
if (logger.isInfoEnabled()) {
logger.info(jobContent);
}
}
/**
* Setter method for property <tt>jobContent</tt>.
*
* @param jobContent value to be assigned to property jobContent
*/
public void setJobContent(String jobContent) {
this.jobContent = jobContent;
}
String xmlPath = System.getProperty("user.dir") +File.separator + "quartz.xml";
System.out.println(FileUtils.readLines(new File(xmlPath)));
new FileSystemXmlApplicationContext(xmlPath);
logger.info("Run QuartzJob");
} catch (Exception e) {
logger.error("Exception occurred during quartz job execution.", e);
}
1、java.util.Timer, java.util.TimerTask
timer啟動一個非同步執行緒執行任務,週期執行任務策略對應方法scheduleAtFixedRate(TimerTask task, long delay, long period),scheduleAtFixedRate(TimerTask task, Date firstTime, long period)可以指定首次任務時間和週期間隔時間。停止任務執行使用cancel()。
Timer執行任務是單執行緒的,內部用任務佇列來維護待執行任務,任務使用最小堆演算法排序(任務下次執行時間距今越小越優先被執行),新增任務時使用鎖機制防止併發問題。但是如果自定義的任務處理邏輯包含訪問競爭資源,則需要自己進行相關競爭資源的防併發處理。
示例程式碼如下:
final Timer timer = new Timer();final AtomicInteger taskExecuteCnt = new AtomicInteger();
TimerTask timerTaskA = new TimerTask() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
timer.cancel();
}
System.out.printf("Task A execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
TimerTask timerTaskB = new TimerTask() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
timer.cancel();
}
System.out.printf("Task B execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
timer.scheduleAtFixedRate(timerTaskA, 0, 500);
timer.scheduleAtFixedRate(timerTaskB, 0, 500);
執行後結果:
Task A execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:24 CST 2015
如上,執行順序為A,B,B,A,A,B,B,A,A,B,B。出現這種同一個任務連續執行是因為Timer內部用佇列維護待處理任務,佇列排序演算法為最小二叉堆(任務下次執行時間距今越小越優先被執行),上述A,B任務的起始執行時間都為0,週期間隔都為500ms。那麼待執行任務佇列堆如下
A
B A1
B1
A執行完後,經過重排堆變為
B
B1 A1
B執行完後,經過重排堆變為
B1
A1
所以就會出現堆重排序導致優先順序相等的任務執行順序不固定,如果需要多個任務以特定順序執行,可通過指定任務首次執行延遲時間來實現,
timer.scheduleAtFixedRate(timerTaskA, 0, 500);
timer.scheduleAtFixedRate(timerTaskB, 1, 500);
如上指定,則同一個週期內A任務永遠先與B任務執行。
另外,如果某個任務執行時間過長,導致下次觸發任務執行時佇列中多個任務下次執行時間已到,則會發生多個任務連續執行。如果任務執行時間對執行結果有影響,則合理指定任務週期,單任務處理的資料量,或者使用非同步處理機制來確保任務執行正確性(任務觸發時只記錄一條任務流水,流水記錄中包括任務觸發時的狀態,後續通過額外的恢復任務處理任務流水,非同步處理邏輯還可以保證在系統故障時不會丟失佇列中待處理的任務)。
2、java.util.concurrent .ScheduledExecutorService
使用java.util.concurrent .ScheduledExecutorService執行任務時,ScheduledExecutorService內部使用執行緒池執行任務,可以指定執行任務的執行緒池大小。併發任務較多時可以配置合適的執行緒池大小提高任務執行效率。
示例程式碼如下:
final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
final AtomicInteger taskExecuteCnt = new AtomicInteger();
Runnable runnableA = new Runnable() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
scheduledExecutorService.shutdownNow();
}
System.out.printf("Task A execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
Runnable runnableB = new Runnable() {
@Override
public void run() {
// scheduler work content
if (taskExecuteCnt.get() >= 10) {
scheduledExecutorService.shutdownNow();
}
System.out.printf("Task B execute,current time:%s\n", new Date());
taskExecuteCnt.incrementAndGet();
}
};
scheduledExecutorService.scheduleWithFixedDelay(runnableA, 0, 500, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleWithFixedDelay(runnableB, 0, 500,TimeUnit.MILLISECONDS);
這裡使用的排程方法是scheduleWithFixedDelay,和scheduleWithFixedRate的區別是:
scheduleWithFixedDelay任務的下次執行時間是根據前次執行結束時間和時間間隔計算得到。
scheduleWithFixedRate任務的下次執行時間是根據前次執行開始時間加時間間隔得到。
使用兩個方法可能帶來的影響是,如果任務執行時間過長,scheduleWithFixedDelay計算得到的待執行任務個數小於scheduleWithFixedRate計算得到的任務個數。
3、Quartz
Quzrtz支援用cron表示式來定義靈活多變的任務觸發時間,例如表示式(0/1 * * * * ?)表示每秒觸發任務執行。
spring quarz任務排程示例程式碼如下:
配置需要的beans,配置檔案為quartz.xml:
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="cronTaskA" class="com.test.CronTask">
<property name="jobContent" value="Task A execute" />
</bean>
<bean id="cronTaskB" class="com.test.CronTask">
<property name="jobContent" value="Task B execute" />
</bean>
<bean id="jobDetailA"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cronTaskA" />
<property name="targetMethod" value="run" />
<property name="concurrent" value="false" />
</bean>
<bean id="jobDetailB"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cronTaskB" />
<property name="targetMethod" value="run" />
<property name="concurrent" value="false" />
</bean>
<bean id="cronTriggerA" class="org.springframework.scheduling.quartz.CronTriggerBean">
<!-- jobDetail -->
<property name="jobDetail" ref="jobDetailA" />
<!-- execute per second -->
<property name="cronExpression" value="0/1 * * * * ?" />
</bean>
<bean id="cronTriggerB" class="org.springframework.scheduling.quartz.CronTriggerBean">
<!-- jobDetail -->
<property name="jobDetail" ref="jobDetailB" />
<!-- execute per second -->
<property name="cronExpression" value="0/1 * * * * ?" />
</bean>
<bean id="schedulerFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerA" />
<ref bean="cronTriggerB" />
</list>
</property>
</bean>
</beans>
自定義任務類實現:
public class CronTask {
/** logger */
private static final Logger logger = LoggerFactory.getLogger(CronTask.class);
/** job content */
private String jobContent;
/**
* task content
*/
public void run() {
if (logger.isInfoEnabled()) {
logger.info(jobContent);
}
}
/**
* Setter method for property <tt>jobContent</tt>.
*
* @param jobContent value to be assigned to property jobContent
*/
public void setJobContent(String jobContent) {
this.jobContent = jobContent;
}
}
任務排程測試程式碼:
String xmlPath = System.getProperty("user.dir") +File.separator + "quartz.xml";
System.out.println(FileUtils.readLines(new File(xmlPath)));
new FileSystemXmlApplicationContext(xmlPath);
logger.info("Run QuartzJob");
} catch (Exception e) {
logger.error("Exception occurred during quartz job execution.", e);
}
相關文章
- Zeus-Master-週期性排程策略實現AST
- 幾種任務排程的 Java 實現方法與比較Java
- Java實現生命週期管理機制Java
- 使用Java實現定時任務排程Java
- java單例的幾種實現方法Java單例
- java幾種代理模式的實現方式Java模式
- PostgreSQL技術週刊第12期:PostgreSQL時空資料排程實踐SQL
- linux系統中的排程週期任務:cronLinux
- 如何在Java中實現非同步任務排程?Java非同步
- 【大資料開發套件排程配置實踐】——不同週期任務依賴配置大資料套件
- 深入 Java Timer 定時排程器實現原理Java
- Java 自動釋放鎖的幾種實現Java
- java定時任務實現的幾種方式Java
- Quartz排程系統入門和排程高可用實現方案quartz
- 幾種排序演算法的原理以及 Java 實現排序演算法Java
- Java建立多執行緒的幾種方式實現Java執行緒
- 詳解 Vue 生命週期實現Vue
- Java面試之Java中實現多執行緒有幾種方法Java面試執行緒
- OS_程式排程:C++實現C++
- Kubernetes 排程器實現初探
- Linux程式排程核心實現分析Linux
- 深入 Java Timer 定時任務排程器實現原理Java
- 任務排程並行演算法的Java簡單實現並行演算法Java
- AOP 有幾種實現方式?
- NIO實現的幾種模型模型
- Java 實現 Map 和 Object 互相轉換的幾種方法JavaObject
- JavaScript實現自定義的生命週期JavaScript
- java servlet 生命週期JavaServlet
- 簡版排程中心搭建及實現思路
- 第 12 期 golang 中 goroutine 的排程Golang
- 負載均衡的幾種演算法Java實現程式碼負載演算法Java
- Java中實現執行緒安全HashSet的幾種方法 | baeldungJava執行緒
- 實現 JavaScript 沙箱的幾種方式JavaScript
- redis加鎖的幾種實現Redis
- 【筆記】時間片輪轉 RR 程式排程演算法(Java 實現)筆記演算法Java
- Java 物件的生命週期Java物件
- celery 與 flask 實現非同步任務排程Flask非同步
- 實現Quartz.NET的HTTP作業排程quartzHTTP