定時任務概括
定時任務是軟體開發中用於自動執行週期性任務的技術。它允許開發者設定特定的時間點或間隔來觸發預定的操作,如資料備份、清理快取、傳送通知等。這種機制可以減少人工干預,提高系統效率和穩定性。定時任務在不同的作業系統和程式設計環境中有不同的實現方式,例如 Linux 的 cron 作業、Windows 的任務計劃程式,或是程式語言中的相關庫。它們幫助自動化重複性工作,確保關鍵任務按時執行,是現代應用程式不可或缺的組成部分。
定時任務使用場景
定時任務在軟體開發當中使用非常廣泛。主要有以下場景:
- 資料備份。定時備份重要資料,防止丟失。
- 日誌清理。定時歸檔和清理日誌檔案,釋放磁碟空間
- 效能監控。定時收集、處理和上報效能資料。
- 資料同步。定時將最新資料同步給其他消費者。
- 資源管理。定時清理、回收系統資源,提升利用率和效能。
當然列舉的這幾個有些寬泛,在實際開發當中,會有多種多樣的定時任務場景。下面我們先來看看 Java
語言都有哪些實現定時任務的類庫。
Java 語言實現定時任務
相信很多小夥伴接觸最多的定時任務就是定時自動化迴歸測試了。通常會有專門的開發和測試框架來完成具體的設定和執行定時任務。在 Java
語言中,實現定時任務有幾種常用的方法:
-
java.util.Timer
類:這是 Java 標準庫提供的一個類,可以用來安排任務以後在後臺執行緒中執行。使用Timer
類,你可以建立一個TimerTask
任務,然後使用schedule
或scheduleAtFixedRate
方法來安排任務的執行。 -
ScheduledExecutorService
介面:這是 Java 併發包中的一部分,提供了更靈活的定時任務排程能力。你可以使用Executors
類建立一個ScheduledExecutorService
例項,然後使用schedule
或scheduleAtFixedRate
方法來安排任務。 -
Spring 框架的
@Scheduled
註解:如果你在使用 Spring 框架,可以利用@Scheduled
註解來簡化定時任務的配置。Spring 的排程器會根據註解的引數來執行相應的方法。 - Quartz Scheduler:這是一個開源的作業排程庫,提供了比 Java 標準庫更強大的定時任務功能。Quartz 允許你配置複雜的排程策略,如 cron 表示式,並支援叢集。
java.util.Timer
利用 Spring
框架支援相對來說比較常見。下面我寫了一個 java.util.Timer
實現每秒列印一次時間的定時任務的簡單案例。可以按以下步驟編寫程式碼:
- 建立一個繼承自
TimerTask
的類,在其中實現run
方法。 - 建立一個
Timer
物件。 - 使用
Timer
物件的schedule
方法安排任務。
以下是具體的示例程式碼:
package com.funtest.temp;
import com.funtester.frame.SourceCode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class FunTester extends SourceCode {
public static void main(String[] args) {
Timer timer = new Timer();// 例項化Timer類
TimerTask task = new TimerTask() {
@Override
public void run() {// 例項化TimerTask類
// 任務程式碼,列印當前時間,並指定執行緒名稱
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
System.out.println(Thread.currentThread().getName() + " " + formatter.format(date));
}
};
// 0表示立即執行,1000表示每隔1秒執行一次
timer.scheduleAtFixedRate(task, 0, 1000);
}
}
下面是控制檯列印的資訊:
Timer-0 10:04:07
Timer-0 10:04:08
Timer-0 10:04:09
Timer-0 10:04:10
使用 java.util.Timer
實現定時任務,雖然具有簡單易用優點,但在我的經驗範圍內極少,大多數都是包裝成服務化,使用 Spring
自帶的定時任務執行。其主要缺點就是:單執行緒執行、異常處理不夠優雅、不支援併發。總體來講不如 ScheduledExecutorService
功能強大。
ScheduledExecutorService
下面是 ScheduledExecutorService
的簡單案例。
package com.funtest.temp;
import com.funtester.frame.SourceCode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class FunTester extends SourceCode {
public static void main(String[] args) {
// 建立一個具有單個執行緒的排程程式
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 建立一個Runnable任務,每秒列印一次當前時間
Runnable task = new Runnable() {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(Thread.currentThread().getName() + " " + sdf.format(new Date()));
}
};
// 從現在開始1秒鐘之後,每隔1秒鐘執行一次
scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
}
}
明顯看到用到了執行緒池的使用,有點不用多說,簡潔性我感覺不輸 java.util.Timer
。
Go 語言定時任務
Go
語言定時任務實現,也是比較簡單的也是利用 Go SDK
自帶的 time
包。下面是簡單的案例:
package main
import (
"fmt"
"time"
)
func main() {
// 建立一個定時器,每隔1秒鐘執行一次
ticker := time.NewTicker(1 * time.Second)
// 延遲關閉定時器
defer ticker.Stop()
// 無限迴圈
for {
// 從定時器的通道中接收資料
select {
case t := <-ticker.C:
fmt.Println("Current time:", t)
}
}
}
這個案例基本是最佳實現,如果大家有需求都可以參考,而且 Go
語言 goroutine
的原因,不用很關心協程效能的問題,我自己就直接使用 goroutine
直接起定時任務了。這種對於複雜的定時任務,這種方式就顯得力不從心了。
在 Go
語言 time
包裡面還有一種方法實現:
package main
import (
"fmt"
"time")
func main() {
// 建立一個定時器,1秒後觸發
timer := time.AfterFunc(1*time.Second, func() {
fmt.Println("Current time:", time.Now())
})
// 防止程式退出
select {
case <-timer.C:
}
}
語法上看好像區別不是很大,各位自己選用吧。
cron 包
下面分享一下 Go
第三方包的實現。cron
是一個在 Go 語言中實現定時任務的流行庫,它允許你使用 cron 表示式來定義任務的執行時間。PS:這個 cron
目測只支援了分鐘級別的。
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3")
func main() {
// 建立一個定時任務
c := cron.New()
// 新增一個定時任務,每隔1分鐘執行一次
c.AddFunc("0/1 * * * *", func() {
fmt.Println("Current time:", time.Now())
})
// 啟動定時任務
c.Start()
// 防止程式退出
select {}
}
在 AddFunc
方法引數 spec
不僅支援 cron
語法,為了方便使用還增加了一個 @every
語法,後面可以跟類似 @every 1m2s
,time.ParseDuration()
支援的格式都可以用在這裡。除此之外 cron
預定義了一些時間規則:
-
@yearly
:也可以寫作@annually
,表示每年第一天的 0 點。等價於0 0 1 1 *
; -
@monthly
:表示每月第一天的 0 點。等價於0 0 1 * *
; -
@weekly
:表示每週第一天的 0 點,注意第一天為週日,即週六結束,週日開始的那個 0 點。等價於0 0 * * 0
; -
@daily
:也可以寫作@midnight
,表示每天 0 點。等價於0 0 * * *
; -
@hourly
:表示每小時的開始。等價於0 * * * *
。
gocron 包
還有一個更加靈活的庫 gocron
功能更加強大,但使用起來存在一定門檻。下面是一個案例:
package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron"
)
func main() {
// 定時任務
s := gocron.NewScheduler(time.Local)
// 每秒執行一次
s.Every(1).Minutes().Do(func() {
fmt.Println("Current time:", time.Now())
})
s.StartBlocking()
fmt.Println("任務結束")
}
gocron
設定時間的話,都可以透過呼叫不同的 API
來組裝實現。對於 API
學習需要一定的學習成本。同時 gocron
也支援 cron
語法的。下面是個例子:
package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron"
)
func main() {
// 定時任務
s := gocron.NewScheduler(time.Local)
// 新增一個每週二上午10點每分鐘執行一次的任務,使用 cron 表示式
s.Cron("0 * 10 ? * 2").Do(func() {
fmt.Println("Task executed at:", time.Now())
})
s.StartBlocking()
fmt.Println("任務結束")
}
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go、Python
- 單元&白盒&工具合集
- 測試方案&BUG&爬蟲&UI 自動化
- 測試理論雞湯
- 社群風采&影片合集