Java 定時任務 & 任務調度

更多 Java 高級知識方面的文章,請參見文集《Java 高級知識》


任務調度是指基于 給定時間點給定時間間隔 或者 給定執(zhí)行次數(shù) 自動執(zhí)行任務。

方式1:通過 Thread 來實現(xiàn)

例如如下的代碼,可以每隔 1000 毫秒做一次打印操作。

public class Job_Schedule_Test1 {
    public static void main(String[] args) {
        new JobThread().start();
    }
}

class JobThread extends Thread {
    public void run() {
        while (true) {
            System.out.println("Test: " + Calendar.getInstance().getTime());

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
    }
}

輸出如下:

Test: Wed Feb 22 14:33:57 CST 2017
Test: Wed Feb 22 14:33:58 CST 2017
Test: Wed Feb 22 14:33:59 CST 2017
Test: Wed Feb 22 14:34:00 CST 2017
Test: Wed Feb 22 14:34:01 CST 2017

方式2:通過 Timer 來實現(xiàn)

  • 自定義一個任務,繼承于 TimerTask,重寫 run 方法
  • 利用 java.util.Timer 實現(xiàn)任務調度
public class Job_Schedule_Test2 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        long delay = 2000;
        long interval = 1000;

        // 從現(xiàn)在開始 2 秒鐘之后啟動,每隔 1 秒鐘執(zhí)行一次
        timer.schedule(new JobTask(), delay, interval);
    }
}

class JobTask extends TimerTask {
    public void run() {
        System.out.println("Test: " + Calendar.getInstance().getTime());
    }
}

輸出如下:

Test: Wed Feb 22 15:02:15 CST 2017
Test: Wed Feb 22 15:02:16 CST 2017
Test: Wed Feb 22 15:02:17 CST 2017
Test: Wed Feb 22 15:02:18 CST 2017
Test: Wed Feb 22 15:02:19 CST 2017

Timer 的設計核心是一個 TaskList 和一個 TaskThread
Timer 將接收到的任務丟到自己的 TaskList 中,TaskList 按照 Task 的最初執(zhí)行時間進行排序。TimerThread 在創(chuàng)建 Timer 時會啟動成為一個守護線程。這個線程會輪詢所有任務,找到一個最近要執(zhí)行的任務,然后休眠,當?shù)竭_最近要執(zhí)行任務的開始時間點,TimerThread 被喚醒并執(zhí)行該任務。之后 TimerThread 更新最近一個要執(zhí)行的任務,繼續(xù)休眠。

Timer 的優(yōu)點在于簡單易用,但由于所有任務都是由同一個線程來調度,因此所有任務都是串行執(zhí)行的,同一時間只能有一個任務在執(zhí)行,前一個任務的延遲或異常都將會影響到之后的任務。
例如我們指定每隔 1000 毫秒執(zhí)行一次任務,但是可能某個任務執(zhí)行花了 5000 毫秒,因此導致后續(xù)的任務并不能按時啟動執(zhí)行。

方式3:通過 ScheduledExecutorService 來實現(xiàn)

鑒于 Timer 的上述缺陷,Java 5 推出了基于線程池設計的 ScheduledExecutorService
其設計思想是,每一個被調度的任務都會 由線程池中一個線程去執(zhí)行,因此任務是并發(fā)執(zhí)行的,相互之間不會受到干擾。
需要注意的是,只有當任務的執(zhí)行時間到來時,ScheduledExecutorService 才會真正啟動一個線程,其余時間 ScheduledExecutorService 都是在輪詢任務的狀態(tài)。

public class Job_Schedule_Test3 {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        long delay = 2;
        long interval = 1;

        // 從現(xiàn)在開始 2 秒鐘之后啟動,每隔 1 秒鐘執(zhí)行一次
        service.scheduleAtFixedRate(
                new JobTask2(), delay,
                interval, TimeUnit.SECONDS);
    }
}

class JobTask2 implements Runnable {
    public void run() {
        System.out.println("Test: " + Calendar.getInstance().getTime());
    }
}

輸出如下:

Test: Wed Feb 22 15:19:05 CST 2017
Test: Wed Feb 22 15:19:06 CST 2017
Test: Wed Feb 22 15:19:07 CST 2017
Test: Wed Feb 22 15:19:08 CST 2017
Test: Wed Feb 22 15:19:09 CST 2017

用 ScheduledExecutorService 和 Calendar 實現(xiàn)復雜任務調度

TimerScheduledExecutor 都僅能提供基于開始時間與重復間隔的任務調度,不能勝任更加復雜的調度需求。
比如,設置每星期二的 16:38:10 執(zhí)行任務。我們可以借助 Calendar 間接實現(xiàn)該功能。
其核心在于根據(jù)當前時間推算出最近一個星期二 16:38:10 的絕對時間,然后計算與當前時間的時間差,作為調用 ScheduledExceutor 函數(shù)的參數(shù)。

具體參見 幾種任務調度的 Java 實現(xiàn)方法與比較

方式4:通過 Quartz 來實現(xiàn)

Quartz is a richly featured, open source job scheduling library that can be integrated within virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; jobs whose tasks are defined as standard Java components that may execute virtually anything you may program them to do.

示例如下:

public class QuartzJob implements org.quartz.Job {
    public QuartzJob() {
    }

    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("Test: " + Calendar.getInstance().getTime());
    }
}

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;

public class Job_Schedule_Test4 {
    public static void main(String[] args) throws SchedulerException {
        // Grab the Scheduler instance from the Factory
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // define the job and tie it to our MyJob class
        JobDetail job = newJob(QuartzJob.class)
                .withIdentity("job1", "group1")
                .build();

        // Trigger the job to run now, and then repeat every 40 seconds
        Trigger trigger = newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())
                .build();

        // Tell quartz to schedule the job using our trigger
        scheduler.scheduleJob(job, trigger);

        // and start it off
        scheduler.start();
    }
}

方式5:通過 JCronTab 來實現(xiàn)

具體參見 幾種任務調度的 Java 實現(xiàn)方法與比較


引用:
Java實現(xiàn)定時任務的三種方法
幾種任務調度的 Java 實現(xiàn)方法與比較

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容