Home  >  Article  >  Java  >  Java scheduled tasks: Use the java Timer class to implement the function of timing tasks

Java scheduled tasks: Use the java Timer class to implement the function of timing tasks

高洛峰
高洛峰Original
2017-01-11 16:07:533626browse

1. Overview

To implement the function of timing task execution in Java, two classes are mainly used, the Timer and TimerTask classes. Among them, Timer is used to execute specified tasks on a background thread according to a specified plan.

TimerTask is an abstract class, and its subclasses represent a task that can be scheduled by Timer. The specific code to be executed is written in the run method that needs to be implemented in TimerTask.

2. Let’s look at the simplest example first

We illustrate through code

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerDemo {
  public static String getCurrentTime() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
  }
 
  public static void main(String[] args) throws InterruptedException {
    System.out.println("main start:"+getCurrentTime());
    startTimer();
    Thread.sleep(1000*5); //休眠5秒
    System.out.println("main  end:"+getCurrentTime());
  }
 
  public static void startTimer(){
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        System.out.println("task  run:"+getCurrentTime());
      }
    };
    Timer timer = new Timer();
    timer.schedule(task, 0);
  }
}

In order to facilitate the observation of information through printing, we added some printing information in the main method , and call Thread.sleep to let the main thread sleep for a while. In addition, a getCurrentTime method for obtaining the current date is added to the class.

The above code, in the startTimer method, first creates a TimerTask object (the task to be executed by the timer), then creates a Timer object, and then calls the schedule method of the Timer class. The Timer class has multiple schedule methods with different parameters. What is used here is:

public void schedule(TimerTask task, long delay)

The meaning of this method is that the timer will delay the delay (milliseconds) before executing the task. If delay is negative or 0, the task will be executed immediately. And it is a one-time execution task, and the task will not be executed repeatedly (or scheduled) in the future.

For the Timer class, a method with the same function is also provided, as follows:

public void schedule(TimerTask task, Date time)

The difference between this method and the above method is that the above method specifies a delay in execution, while this method specifies Executed at a specific point in time. Note that if the current time of the system has exceeded the time specified by the parameter time, the task will be executed immediately.

When running the above code, we found that the program immediately printed two pieces of information similar to the following:

main start:2016-01-13 22:23:18
task run:2016-01-13 22:23:18

Because the delay parameter value we pass to the schedule method here is 0, the task will be executed immediately, so the time printed by the two statements is the same ,this is necessary. You can change the incoming delay value yourself to see changes in the output information. After about 5 seconds (that is, the sleep time), another message was printed:

main end:2016-01-13 22:23:23

The time of printing the message is the same as The difference in the above statement is 5 seconds, which is consistent with the sleep setting and is very reasonable.

But we will find a very interesting phenomenon. We will find that the process will not exit. At this time, the main thread has ended. This shows that after the timer completes the task, even if there are no tasks waiting to be executed later. , the background thread created in the timer will not exit immediately. I checked the relevant java doc document and explained that the timer thread will not actively exit and needs to wait for garbage collection. However, Java's waiting for garbage collection cannot be controlled by the code itself, but is controlled by the virtual machine.

After some research, I found that the timer thread will be created when the Timer object is created and the Timer timer = new Timer(); statement is executed. That is to say, even if the above code does not contain the statement timer.schedule(task, 0);, the program will not exit. This feels quite unreasonable. I studied the source code of the Timer class again and found that it also has a constructor with Boolean parameters:

public Timer(boolean isDaemon)

As you can see from the parameter name, if the parameter value is true, the timer created by Timer The thread is a daemon thread. The meaning of daemon thread is that when all working threads in the java process exit, the daemon thread will automatically exit.

At this time, we only need to change the code for creating the Timer object in the above example to: Timer timer = new Timer(true);

It is found that after running the program, wait for the main thread (the main thread is not After the daemon thread (which is a working thread) ends, the program will exit, which means that the timer thread also exits. This means that after adding the parameter true, the created thread is a daemon thread.

But the problem is that in real application scenarios, there are many worker threads running and the program will not exit casually. So what should we do if we want the timer to exit or close immediately? We will introduce this below.

3. Exiting the timer

The Timer class provides a cancel method to cancel the timer. Calling the cancel method will terminate this timer and discard all currently scheduled tasks. This does not interfere with the currently executing task (if one exists). Once the timer is terminated, its execution thread is also terminated, and no more tasks can be scheduled against it.

Note that by calling this method within the run method of the timer task called by this timer, you can absolutely ensure that the task being executed is the last task executed by this timer. This method can be called repeatedly; however, the second and subsequent calls have no effect.

Let’s look at another example code:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerDemo {
  public static String getCurrentTime() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
  }
 
  public static void main(String[] args) throws InterruptedException {
    System.out.println("main start:"+getCurrentTime());
    Timer timer = startTimer();
    Thread.sleep(1000*5); //休眠5秒
    System.out.println("main  end:"+getCurrentTime());
    timer.cancel();
  }
 
  public static Timer startTimer(){
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        System.out.println("task  run:"+getCurrentTime());
      }
    };
    Timer timer = new Timer();
    timer.schedule(task, 0);
    return timer;
  }
}

When running the program, the output is exactly the same as the above example. The difference is that after the main method ends. The process will actively exit, which means that the timer thread has been closed.

Because we called the cancel method in the main method. Note that if you do not call the cancel method in the run method of TimerTask, you must pay attention to make sure that the task you want to execute has started or completed, otherwise if the task has not started executing. Just call cancel, and all tasks will not be executed. For example, the above code,

比如上面的代码,如果我们不在main方法中调用cancel方法,而是在startTimer方法中 timer.schedule(task, 0); 语句后加上timer.cancel();语句,运行后会发现,定时器任务不会被执行,因为还未来得及执行就被取消中止了。

四、定时执行任务

上面的例子,我们介绍的是一次性任务,也就是定时器时间到了,执行完任务,后面不会再重复执行。在实际的应用中,有很多场景需要定时重复的执行同一个任务。这也分两种情况,一是每隔一段时间就执行任务,二是每天(或每周、每月等)的固定某个(或某几个)时间点来执行任务。

我们先来看第一种情况,实现每隔10秒执行同一任务的例子。代码如下:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerDemo {
  public static String getCurrentTime() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
  }
 
  public static void main(String[] args) throws InterruptedException {
    System.out.println("main start:"+getCurrentTime());
    startTimer();
  }
 
  public static void startTimer(){
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        System.out.println("task  run:"+getCurrentTime());
        try {
          Thread.sleep(1000*3);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    };
    Timer timer = new Timer();
    timer.schedule(task, 1000*5,1000*10);
  }
}

执行上述程序,输出信息如下(因为定时器没有停止,重复执行任务,会不断输出,这里只拷贝了前面的一些输出)

main start:2016-01-14 08:41:14
task   run:2016-01-14 08:41:19
task   run:2016-01-14 08:41:29
task   run:2016-01-14 08:41:39
task   run:2016-01-14 08:41:49
task   run:2016-01-14 08:42:00
task   run:2016-01-14 08:42:10
task   run:2016-01-14 08:42:20
task   run:2016-01-14 08:42:30
task   run:2016-01-14 08:42:40

在上面的代码中,我们调用了 timer.schedule(task, 1000*5,1000*10); 这个含义是该任务延迟5秒后执行,然后会每隔10秒重复执行。我们观察输出信息中打印的时间,是与预期一样的。 另外可以看出,间隔是以任务开始执行时间为起点算的,也就是并不是任务执行完成后再等待10秒。

Timer类有两个方法可以实现这样的功能,如下:

public void schedule(TimerTask task, long delay, long period)
 
public void schedule(TimerTask task, Date firstTime, long period)

我们上面代码用的是第一个方法。两个方法区别在于第一次执行的时间,第一个方法是在指定延期一段时间(单位为毫秒)后执行;第二个方法是在指定的时间点执行。

这时我们考虑如下场景,如果某个任务的执行耗时超过了下次等待时间,会出现什么情况呢? 我们还是通过代码来看:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerDemo {
  public static String getCurrentTime() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
  }
 
  public static void main(String[] args) throws InterruptedException {
    System.out.println("main start:"+getCurrentTime());
    startTimer();
  }
 
  public static void startTimer(){
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        System.out.println("task begin:"+getCurrentTime());
        try {
          Thread.sleep(1000*10);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("task  end:"+getCurrentTime());
      }
    };
    Timer timer = new Timer();
    timer.schedule(task, 1000*5,1000*5);
  }
}

与前面代码相比,我们只改了2处代码和修改了下打印,一是将run方法中的sleep改为了10秒,二是将任务的执行周期改为5秒。也就说任务的执行耗时超过了任务重复执行的间隔。运行程序,前面的输出如下:

main start:2016-01-14 09:03:51
task begin:2016-01-14 09:03:56
task   end:2016-01-14 09:04:06
task begin:2016-01-14 09:04:06
task   end:2016-01-14 09:04:16
task begin:2016-01-14 09:04:16
task   end:2016-01-14 09:04:26
task begin:2016-01-14 09:04:26
task   end:2016-01-14 09:04:36
task begin:2016-01-14 09:04:36
task   end:2016-01-14 09:04:46
task begin:2016-01-14 09:04:46
task   end:2016-01-14 09:04:56

可以看出,每个任务执行完成后,会立即执行下一个任务。因为从任务开始执行到任务完成的耗时已经超过了任务重复的间隔时间,所以会重复执行。

五、定时执行任务(重复固定时间点执行)

我们来实现这样一个功能,每天的凌晨1点定时执行一个任务,这在很多系统中都有这种功能,比如在这个任务中完成数据备份、数据统计等耗时、耗资源较多的任务。代码如下:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
 
public class TimerDemo {
  public static String getCurrentTime() {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
  }
 
  public static void main(String[] args) throws InterruptedException {
    System.out.println("main start:" + getCurrentTime());
    startTimer();
  }
 
  public static void startTimer() {
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        System.out.println("task begin:" + getCurrentTime());
        try {
          Thread.sleep(1000 * 20);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("task  end:" + getCurrentTime());
      }
    };
    Timer timer = new Timer();
    timer.schedule(task, buildTime(), 1000 * 60 * 60 * 24);
  }
 
  private static Date buildTime() {
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 1);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    Date time = calendar.getTime();
    if (time.before(new Date())) {
      //若果当前时间已经是凌晨1点后,需要往后加1天,否则任务会立即执行。
      //很多系统往往系统启动时就需要立即执行一次任务,但下面又需要每天凌晨1点执行,怎么办呢?
      //很简单,就在系统初始化话时单独执行一次任务(不需要用定时器,只是执行那段任务的代码)
      time = addDay(time, 1);
    }
    return time;
  }
 
  private static Date addDay(Date date, int days) {
    Calendar startDT = Calendar.getInstance();
    startDT.setTime(date);
    startDT.add(Calendar.DAY_OF_MONTH, days);
    return startDT.getTime();
  }
 
}

因为是间隔24小时执行,没法等待观察输出。

六、小结

本文介绍了利用java Timer类如何执行定时任务的机制。可以看出,还是有许多需要注意的方法。 本文中介绍的例子,每个定时器只对应一个任务。

本文介绍的内容可以满足大部分应用场景了,但还有一些问题,比如对于一个定时器包括多个任务?定时器取消后能否再次添加任务?Timer类中还有哪些方法可用? 这些问题,我们再后面的博文中介绍。

更多Java定时任务:利用java Timer类实现定时执行任务的功能相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn