Java 定時任務


摘自:https://www.cnblogs.com/Howlet/p/15580411.html

Java 定時任務

 

還沒真正的遇到使用定時任務的場景,不管怎么說先學起來


1. 定時任務

很多情況下任務並非需要立即執行,而是需要往后或定期執行,這不可能人工去操作,所以定時任務就出現了。項目中肯定會用到使用定時任務的情況,筆者就需要定時去拉取埋點數據


使用定時任務的情況:

  • 每周末凌晨備份數據
  • 觸發條件 5 分鍾后發送郵件通知
  • 30 分鍾未支付取消訂單
  • 每 1 小時去拉取數據
  • ......






2. Thread實現

筆試中首次遇到定時任務急急忙忙想出來的方法


2.1 使用

public class ThreadSchedule { public static void main(String[] args) { // 5 秒后執行任務 int interval = 1000 * 5; // 新線程執行 new Thread(() -> { try { Thread.sleep(interval); System.out.println("執行定時任務"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } 


2.2 分析

  • 定時不准確,因依賴底層硬件,Windows誤差為10微妙
  • System.currentTimeMillis() 依賴系統硬件,還會受網絡時間同步修改
  • System.nanoTime() 依賴 JVM 的運行納秒數,並不受同步影響,適用於計算准確的時間差
  • 但計算當前日期還是要使用 currentTimeMillis 的格林威治時間,而 nanoTime 計算 JVM 運行時間不准確






3. java.util.Timer

Timer 負責執行計划功能,會啟動一個后台線程,而 TimerTask 負責任務邏輯


3.1 使用

public class TimeSchedule { public static void main(String[] args) { // 啟動一個守護線程 Timer timer = new Timer(); // 定時任務 TimerTask timerTask = new TimerTask() { @Override public void run() { System.out.println("執行定時任務"); } }; long delay = 0; // 延遲執行 long period = 1000 * 5; // 間隔 timer.scheduleAtFixedRate(timerTask, 1, period); // timer.cancel(); 可取消 } } 


3.2 分析

// new Timer() 最終的構造函數會啟動一個線程 public Timer(String name) { thread.setName(name); thread.start(); } // 這個線程里面封裝了一個 Queue 優先級隊列,該線程會去隊列里不停執行里面的任務 class TimerThread extends Thread { private TaskQueue queue; TimerThread(TaskQueue queue) { this.queue = queue; } } // 這個隊列里面存放了各種 TimerTask 定時的任務邏輯 class TaskQueue { private TimerTask[] queue = new TimerTask[128]; } 
  • 只有一個單線程執行,所以是串行執行
  • 某個任務執行時間較長會阻塞后面預定執行的任務,所以時間並不准確
  • 線程報錯后續的定時任務直接停止






4. ScheduledExecutorService

java.util.concurrent中的工具類,是一個多線程的定時器


4.1 使用

public class ExecutorSchedule { public static void main(String[] args) { // 定時任務 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("執行定時任務"); } }; // 線程池執行 long delay = 0; // 延遲執行 long period = 1000 * 5; // 間隔 ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS); } 

筆者最常用的一個定時操作了,之前還寫過定時的探測任務







5. Spring的定時任務

需要開啟定時功能@EnableScheduling

@Component public class SpringSchedule { // cron 表達式,每秒執行一次 @Scheduled(cron = "*/1 * * * * ?") public void springSchedule(){ System.out.println("執行定時任務"); } } 

底層是 ScheduledThreadPoolExecutor 線程池,和上面的 ScheduledExecutorService 是同根同源







6. XXL-JOB

xxl-job 是個人維護的分布式任務調度框架(國人寫的,有詳細的中文文檔),分為 調度中心 和 執行器。執行器就是定時任務,而調度中心則負責管理調用這些定時任務,調度中心也可以存儲定時任務通過腳本形式(Java 是 Grovvy)免編譯地實時下發到各服務中執行。最重要的是有 UI 界面,用戶友好的體驗


6.1 建立數據庫

xxl-job 的存儲是基於數據庫的,相對比 quartz 可保存在內存和數據庫有一點性能影響。首先第一步就是要建庫,在 xxl-job 官網有 SQL 語句 tables_xxl_job.sql,直接執行即可建庫建表



6.2 部署 xxl-job-admin 調度中心

從 Git 上拉取最新的代碼,然后編譯根模塊,填好 admin 模塊的數據庫地址等,即可啟動這個調度中心(支持 Docker 部署,更加方便)



6.3 創建定時任務

在需要定時任務的服務中 引入依賴、添加配置、創建定時任務



6.3.1 依賴

<!-- xxl-job-core --> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>${project.parent.version}</version> </dependency> 

6.3.2 基本配置

# xxl-job admin address xxl.job.admin.addresses=http://xxx.xxx.xxx:8080/xxl-job-admin  # xxl-job executor appname xxl.job.executor.appname=xxl-job-executor-demo 

6.3.3 定時任務

@Component public class MyJob { @XxlJob("MyJob") public void MyJob() throws Exception { // 執行器日志記錄 XxlJobHelper.log("myjob is execute"); // 定時任務邏輯 System.out.println("myjob is executing"); // default success } } 


6.4 執行定時任務

進入調度中心新建一個任務,然后執行定時任務即可(使用的是 RPC 遠程過程調用)



6.5 遇到的問題

默認執行器是自動注冊到調度中心的,但是時常進去的地址有問題而導致執行失敗,所以要手動錄入執行器的地址



6.6 分析

作為輕量級的分布式定時任務,有 UI 界面簡單方便使用,而且對代碼沒什么侵入性,已經能滿足大部分項目的需求了,筆者如果要用定時任務也會首選 xxl-job



 
分類:  架構設計
好文要頂  關注我  收藏該文   
1
0
 
 
 
« 上一篇:  SpringBoot Actuator — 埋點和監控
» 下一篇:  Java的腳本機制、編譯器API
posted @  2021-11-20 11:00  Howlet  閱讀(242)  評論(0)  編輯  收藏  舉報

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM