1. 總結常見的實現定時任務的幾種方法
- thread實現 【原理:通過創建一個線程,讓他在while循環里面一直運行,用sleep() 方法讓其休眠從而達到定時任務的效果。】
- Timer類
- ScheduledExcecutorService類
- 使用spring的 spring-task 實現
- Quartz
以下演示幾種實現方式:每隔一秒打印一次hello world
1.1 thread實現
public static void main(String[] args) {
final long timeInterval = 1000;
Runnable runnable = new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("hello world");
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
1.2 Timer類實現
Timer是jdk中自帶的一個定時器工具,使用的時候會在主線程之外起一個單獨的線程執行指定的計划任務,可以指定執行一次或者反復執行多次。但封裝任務的類是TimerTask類(實際該類是一個抽象類,執行任務的代碼要放在該類的子類中)。
TimerTask是一個實現了Runnable接口的抽象類,代表一個可以被Timer執行的任務。
構造方法:

成員方法:

public static void main(String[] args) {
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("Hello!Word!");
}
};
Timer timer = new Timer();
long delay = 0;
long intevalPeriod = 1 * 1000;
timer.scheduleAtFixedRate(task, delay, intevalPeriod);
}
schedule 與 scheduleAtFixedRate區別:
schedule會保證任務的間隔是按照定義的period參數嚴格執行的,如果某一次調度時間比較長,那么后面的時間會順延,保證調度間隔都是period。
scheduleAtFixedRate是嚴格按照調度時間來的,如果某次調度時間太長了,那么會通過縮短間隔的方式保證下一次調度在預定時間執行。
線程安全, 但只會單線程執行, 如果執行時間過長, 就錯過下次任務了, 拋出異常時, timerWork會終止
啟動和取消任務是可以控制的
1.3 ScheduledExcecutorService類實現
ScheduledExecutorService是JDK1.5以后java.util.concurrent中的一個接口, 用於實現定時任務。
public static void main(String[] args) {
Runnable r1 = new Runnable() {
public void run() {
System.out.println("Hello Word");
}
};
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 第二個參數為首次執行的延時時間,第三個參數為定時執行的間隔時間
service.scheduleAtFixedRate(r1, 3, 1, TimeUnit.SECONDS);// 3秒后開始執行
}
他是通過線程池的方式執行任務,可以多線程執行。
啟動和取消任務是可以控制的
可以設定第一次的延遲時間
1.4 使用spring提供的 spring-task 實現
a) 只需要導入web的starter依賴
b) 在啟動類上添加 @EnableScheduling 注解
c) 用 @Scheduled 注解開啟一個定時任務。
@Component
public class SchedulerTask {
private int count = 0;
/**
* @Author Smith
* @Description 設置每1秒執行一次
* @Date 14:23 2019/1/24
* @Param
* @return void
**/
@Scheduled(cron = "*/1 * * * * ?") // 基於cron表達式實現
private void process(){
System.out.println("hello world " + (count++));
}
}
@Scheduled 該注解的常用參數說明【不基於cron表達式實現的時候】:
fixedRate 表示任務執行之間的時間間隔,具體是指兩次任務的開始時間間隔,即第二次任務開始時,第一次任務可能還沒結束。
fixedDelay 表示任務執行之間的時間間隔,具體是指本次任務結束到下次任務開始之間的時間間隔。
initialDelay 表示首次任務啟動的延遲時間。
所有時間的單位都是毫秒。
1.5 Quartz
參考
源碼
Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它可以與J2EE與J2SE應用程序相結合也可以單獨使用。Quartz可以用來創建簡單或為運行十個,百個,甚至是好幾萬個Jobs這樣復雜的程序。
Quartz默認是多線程異步執行,單個任務時,在上一個調度未完成時,下一個調度時間到時,會另起一個線程開始新的調度。
Quartz支持集群定時任務
Quartz與Spring Task區別
- Quartz默認多線程異步執行,Task默認單線程同步執行。
- Quartz單個任務時,在上一個調度未完成時,下一個調度時間到時,會另起一個線程開始新的調度。Task單個任務時,當前次的調度完成后,再執行下一次任務調度。
- Quartz多個任務時,任務之間沒有直接影響,多任務執行的快慢取決於CPU的性能。Task多個任務時,一個任務執行完成后才會執行下一個任務。若需要任務能夠並發執行,需手動設置線程池
- Quartz可以采用集群方式,分布式部署到多台機器,分配執行定時任務
兩者對比總結:
1、實現,Task注解實現方式,比較簡單。Quartz需要手動配置Jobs。
2、任務執行,Task默認單線程串行執行任務,多任務時若某個任務執行時間過長,后續任務會無法及時執行。Quartz采用多線程,無這個問題。
3、調度,Task采用順序執行,若當前調度占用時間過長,下一個調度無法及時執行;
4、Quartz采用異步,下一個調度時間到達時,會另一個線程執行調度,不會發生阻塞問題,但調度過多時可能導致數據處理異常
5、部署,Quartz可以采用集群方式,分布式部署到多台機器,分配執行定時任務
Quartz 核心概念:
a) Job 表示一個工作,要執行的具體內容。此接口中只有一個方法。Task多個任務時,一個任務執行完成后才會執行下一個任務。若需要任務能夠並發執行,需手動設置線程池。
void execute(JobExecutionContext context)
b) JobDetail 表示一個具體的可執行的調度程序,Job 是這個可執行程調度程序所要執行的內容,另外 JobDetail 還包含了這個任務調度的方案和策略。
c) Trigger 代表一個調度參數的配置,什么時候去調。
d) Scheduler 代表一個調度容器,一個調度容器中可以注冊多個 JobDetail 和 Trigger。當 Trigger 與 JobDetail 組合,就可以被 Scheduler 容器調度了。
Spring Boot整合Quartz:
- 導入Quartz依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 編寫任務類
public class Task implements Job {
// 任務類不能直接注入Bean,若想注入Bean需要配置第4步
@Autowired
private TestService service;
private void before(){
System.out.println("定時任務開始");
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
service.testDemo();
System.out.println("hello quartz");
}
private void afer(){
System.out.println("定時任務結束");
}
}
★ 任務類不能直接注入Bean,若想注入Bean需要配置第4步 ★
- 編寫Quartz配置
@Configuration
public class QuartzConfig {
@Bean
public JobDetailFactoryBean jobDetailFactoryBean() {
JobDetailFactoryBean factory = new JobDetailFactoryBean();
//關聯我們自己的Job類
factory.setJobClass(Task.class);
return factory;
}
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
CronTriggerFactoryBean factory = new CronTriggerFactoryBean();
factory.setJobDetail(jobDetailFactoryBean.getObject());
//設置觸發時間
factory.setCronExpression("0/2 * * * * ?");
return factory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean, MyAdaptableJobFactory jobFactory) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
//關聯trigger
factory.setTriggers(cronTriggerFactoryBean.getObject());
factory.setJobFactory(jobFactory);
return factory;
}
}
- 編寫配置【用於任務類能注入我們的bean】
@Component
public class MyAdaptableJobFactory extends AdaptableJobFactory {
// 可以將一個對象添加到SpringIOC 容器中,並且完成該對象注入
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 該方法需要將實例化的任務對象手動的添加到springIOC 容器中並且完成對象的注入
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object obj = super.createJobInstance(bundle);
//將obj 對象添加Spring IOC 容器中,並完成注入
this.autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
2 Quartz[群集環境]
使用quartz實現定時任務[單機版],若是部署多台機器,那么到了時間點,多台服務器便會同時均開始執行定時任務。
Quartz是能適用於分布式集群環境的,在同一時間只會有一台機器執行定時任務。
Quartz 中集群如何工作:
一個 Quartz 集群中的每個節點是一個獨立的 Quartz 應用,它又管理着其他的節點。意思是你必須對每個節點分別啟動或停止。不像許多應用服務器的集群,獨立的 Quartz 節點並不與另一其的節點或是管理節點通信。Quartz 應用是通過數據庫表來感知到另一應用的。離開了db將無法感知
2.1 導入依賴
<!-- spring boot2.x + quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.2 數據庫建表
到官網下載
下載之后解壓,進入如下目錄,創建數據庫表:


11張表功能說明:

