当前位置: 代码迷 >> 综合 >> Java原生API的任务调度方式------Timer、ScheduledExecutorService、Calendar
  详细解决方案

Java原生API的任务调度方式------Timer、ScheduledExecutorService、Calendar

热度:9   发布时间:2023-12-20 23:29:54.0

方式一、使用java.util.Timer和java.util.TimerTask创建任务简单的任务调度。

1、其中Timer负责设定TimerTask的起始时间和间隔执行时间。只需要继承TimerTask,实现自己的run()方法,然后将其交给Timer去定时执行就可以了。

2、Timer的设计核心是一个TaskList和一个TaskThread。Timer将接收到的任务压入TaskList,任务执行排序是按照最初的执行时间排序的。

3、TimerThread 在创建 Timer 时会启动成为一个守护线程。这个线程会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。之后 TimerThread 更新最近一个要执行的任务,继续休眠。

4、Timer 的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务

public class TimerTest extends TimerTask {
    private String taskName = "";

    public TimerTest(String taskName ) {
        this.taskName = taskName ;
    }

    @Override
    public void run() {
        System.out.println("execute" + taskName );
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        long delay1 = 1 * 1000;
        long period1 = 1000;
        timer.schedule(new TimerTest("task1 start......"), delay1, period1);
        long delay2 = 2 * 1000;
        long period2 = 2000;
        timer.schedule(new TimerTest("task2 start......"), delay2, period2);
    }
}

方式二、使用java.util.concurrent.ScheduledExecutorService实现任务调度。

1、ScheduleAtFixedRate()每次执行时间为 :initialDelay, initialDelay+period, initialDelay+2*period, …

2、ScheduleWithFixedDelay()每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay,...

3、每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。

4、需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。

public class ScheduledExecutorTest implements Runnable {
    private String taskName = "";

    public ScheduledExecutorTest(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println("execute" + taskName);
    }

    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        long initialDelay1 = 1;
        long period1 = 1;
        service.scheduleAtFixedRate(new ScheduledExecutorTest("task1 start......."), initialDelay1, period1,
                TimeUnit.SECONDS);
        long initialDelay2 = 1;
        long delay2 = 1;
        service.scheduleWithFixedDelay(new ScheduledExecutorTest("task2 start......."), initialDelay2, delay2,
                TimeUnit.SECONDS);
    }
}

方式三、用 ScheduledExecutor 和 Calendar 实现复杂任务调度。

1、Timer 和 ScheduledExecutor 都仅能提供基于开始时间与重复间隔的任务调度,不能胜任更加复杂的调度需求。比如,设置每星期二的 16:38:10 执行任务。该功能使用 Timer 和 ScheduledExecutor 都不能直接实现,但我们可以借助 Calendar 间接实现该功能。

public class ScheduledExceutorTest2 extends TimerTask {
    private String taskName = "";

    public ScheduledExceutorTest2(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println("execute" + taskName);
    }

    /**
     * 计算从当前时间currentDate开始,满足条件dayOfWeek, hourOfDay, minuteOfHour,
     * secondOfMinite的最近时间
     * 
     * @param currentDate
     * @param dayOfWeek
     * @param hourOfDay
     * @param minuteOfHour
     * @param secondOfMinite
     * @return
     */
    public Calendar getEarliestDate(Calendar currentDate, int dayOfWeek, int hourOfDay, int minuteOfHour,
            int secondOfMinite) {
        int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR);// 当前时间是一年中的第几周
        int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK);// 周
        int currentHour = currentDate.get(Calendar.HOUR_OF_DAY);// 时
        int currentMinute = currentDate.get(Calendar.MINUTE);// 分
        int currentSecond = currentDate.get(Calendar.SECOND);// 秒
        boolean weekLater = false;
        if (dayOfWeek < currentDayOfWeek) {
            weekLater = true;// 输入DAY_OF_WEEK小于当前DAY_OF_WEEK
        } else if (dayOfWeek == currentDayOfWeek) {
            if (hourOfDay < currentHour) {
                weekLater = true;// DAY_OF_WEEK相等,输入HOUR_OF_DAY小于当前HOUR_OF_DAY
            } else if (hourOfDay == currentHour) {
                if (minuteOfHour < currentMinute) {
                    weekLater = true;// DAY_OF_WEEK相等,DAY_OF_WEEK相等,输入MINUTE小于当前MINUTE
                } else if (minuteOfHour == currentSecond) {
                    if (secondOfMinite < currentSecond) {
                 weekLater = true;// DAY_OF_WEEK相等,DAY_OF_WEEK相等,MINUTE相等,输入SECOND小于当前SECOND
                    }
                }
            }
        }
        if (weekLater) {
            // 设置当前日期中的WEEK_OF_YEAR为当前周推迟一周
            currentDate.set(Calendar.WEEK_OF_YEAR, currentWeekOfYear + 1);
        }
        // 设置当前日期中的DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND为输入条件中的值。
        currentDate.set(Calendar.DAY_OF_WEEK, dayOfWeek);
        currentDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
        currentDate.set(Calendar.MINUTE, minuteOfHour);
        currentDate.set(Calendar.SECOND, secondOfMinite);
        return currentDate;
    }

    public static void main(String[] args) {
        ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1");
        // 获取当前时间
        Calendar currentDate = Calendar.getInstance();
        long currentDateLong = currentDate.getTime().getTime();
        System.out.println("Current Date = " + currentDate.getTime().toString());
        // 计算满足条件的最近一次执行时间
        Calendar earliestDate = test.getEarliestDate(currentDate, 3, 16, 38, 10);
        long earliestDateLong = earliestDate.getTime().getTime();
        System.out.println("Earliest Date = " + earliestDate.getTime().toString());
        // 计算从当前时间到最近一次执行时间的时间间隔
        long delay = earliestDateLong - currentDateLong;
        // 计算执行周期为一星期
        long period = 7 * 24 * 60 * 60 * 1000;
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        // 从现在开始delay毫秒之后,每隔一星期执行一次job1
        service.scheduleAtFixedRate(test, delay, period, TimeUnit.MILLISECONDS);

    }
}

  相关解决方案