Java 定时器和定时器任务

原文:https://www.studytonight.com/java-examples/java-timer-and-timertask

Java 中的计时器用于调度任务。时间任务是我们想要执行的任务。我们可以在 TimerTask 的帮助下定义一个任务,并使用 Timer 对其进行调度。java.util.Timer类使用后台线程来调度任务。

TimerTask也是 java.util 的一部分,是一个实现 Runnable接口的抽象类。我们需要覆盖 run() 方法来定义任务。

在本教程中,我们将学习如何使用计时器安排时间任务。

Timer and TimerTask in Java

延迟后计划任务

Timer 类的 schedule() 方法用于通过使用线程来调度未来的任务。这个方法需要一个 TimerTask 实例和一个延迟,之后我们要执行任务。

如果我们想立即执行任务,那么我们可以传递零作为第二个参数。请注意,延迟以毫秒为单位。

import java.util.Timer;
import java.util.TimerTask;

public class Demo
{
    public static void main(String[] args)
    {
        TimerTask t = new TimerTask() {
            public void run()
            {
                System.out.println("Performing the task(takes 2000ms)");
                try {
                    Thread.sleep(2000);
                } 
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Task Completed");
            }
        };

        Timer timer = new Timer();
        timer.schedule(t, 1000);//Scheduling the task after a delay of 1000ms
    }
}

执行任务(耗时 2000 毫秒) 任务完成

我们也可以新建一个类,扩展TimerTask类。请注意,多个定时器任务可以共享一个定时器,因为定时器类是线程安全的,不需要任何外部同步。

让我们创建两个用户定义类的实例,并使用一个计时器对它们进行调度。

import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }

    @Override
    public void run()
    {
        System.out.println("Performing the Addition(takes 2000ms)");
        try {
            Thread.sleep(2000);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("The sum is: " + (a+b));
    }    
}
public class Demo
{
    public static void main(String[] args)
    {
        AdditionTask t1 = new AdditionTask(10, 20);
        AdditionTask t2 = new AdditionTask(40, 60);
        Timer timer = new Timer();
        timer.schedule(t1, 0);//Scheduling the first task without any delay
        timer.schedule(t2, 5000);//Scheduling the second task after a delay of 5000ms
    }
}

执行加法(耗时 2000 毫秒) 总和为:30 执行加法(耗时 2000 毫秒) 总和为:100

另一件需要注意的事情是,即使在任务完成后,我们的程序也会继续运行。要终止我们的程序,我们可以在 run()方法中使用 System.exit(0)

@Override
public void run()
{
    System.out.println("Performing the Addition(takes 2000ms)");
    try {
        Thread.sleep(2000);
    } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("The sum is: " + (a+b));
    System.exit(0);
}

按日期和时间安排任务

schedule()方法可以采用一个日期对象来代替延迟。计时器将在该日期和时间安排任务。下面的代码演示了这一点。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }

    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
    }    
}
public class Demo
{
    public static void main(String[] args)
    {
        Date date = new Date();
        System.out.println("Current Date: " + date);

        date.setTime(date.getTime() + 5000);//increasing the time by 5000ms

        AdditionTask t = new AdditionTask(10, 20);
        Timer timer = new Timer();
        timer.schedule(t, date);//Scheduling the task on the given date
    }
}

当前日期:2021 年 8 月 04 日星期三 19:19:32 IST 执行任务时间:2021 年 8 月 04 日星期三 19:19:37 IST 总计:30

安排可重复的任务

我们可以将第三个参数传递给 schedule()方法,以便在某个时间段后重复该任务。第三个参数表示任务两次出现之间的时间间隔(毫秒)。

该时间间隔表示前一任务发生后的固定延迟。时间间隔是相对的,因为它是在前一个任务完成后计算的。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }
    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
    }
}
public class Demo
{
    public static void main(String[] args)
    {
        AdditionTask t = new AdditionTask(10, 20);

        long delay = 100;
        long repeatPeriod = 1000;
        Timer timer = new Timer();
        timer.schedule(t, delay, repeatPeriod);//Repeat task every 1000ms
    }
}

执行任务时间:2021 年 8 月 05 日星期四 08:55:19 IST 执行任务时间:2021 年 8 月 05 日星期四 08:55:20 IST 执行任务时间:2021 年 8 月 05 日星期四 08:55:21 T4 执行任务时间:2021 年 8 月 05 日星期四 08:55:22

我们也可以使用 scheduleAtFixedRate() 方法以固定的速率调度任务。这将使用绝对时间来安排任务。如果其中一个任务被延迟,这个方法不会影响其他任务。

在这种情况下,时间差距是绝对的,不会受到前一个任务完成时间的影响。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }
    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
    }    
}
public class Demo
{
    public static void main(String[] args)
    {
        AdditionTask t = new AdditionTask(10, 20);

        long delay = 100;
        long repeatPeriod = 1000;
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(t, delay, repeatPeriod);//Repeat task every 1000ms
    }
}

执行任务时间:2021 年 8 月 04 日星期三 19:21:46 IST 执行任务时间:2021 年 8 月 04 日星期三 19:21:47 IST 执行任务时间:2021 年 8 月 04 日星期三 19:21:48 IST 执行任务时间:2021 年 8 月 04 日星期三 19:21:49 IST

我们可以使用这些方法(有适当的重复周期)每分钟、每小时、每天执行一项任务,等等。

取消任务

我们可以在 TimerTasks 的 run()方法中使用 cancel() 方法,在一次执行后取消它们。

在下面的代码中,我们安排了一个每秒重复一次的任务。但是由于 cancel()方法,任务将只执行一次。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }
    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
        cancel();//cancel after one execution
    }    
}
public class Demo
{
    public static void main(String[] args)
    {
        AdditionTask t = new AdditionTask(10, 20);

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(t, 1000, 1000);//Repeat task every 1000ms
    }
}

执行任务时间:2021 年 8 月 04 日星期三 19:24:10 IST 总计:30

我们也可以使用 Timer.cancel() 方法取消一个预定的任务。我们可以让线程休眠几秒钟,至少执行一次任务。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }
    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
    }    
}
public class Demo
{
    public static void main(String[] args) throws InterruptedException
    {
        AdditionTask t = new AdditionTask(10, 20);

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(t, 0, 1000);//Start after a delay of 1000 and Repeat task every 1000ms
        Thread.sleep(1);
        timer.cancel();//Canceling the timer
    }
}

执行任务时间:2021 年 8 月 04 日星期三 19:26:44 IST 总计:30

执行器服务

我们还可以使用执行器服务来安排时间任务。

import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

class AdditionTask extends TimerTask
{
    int a;
    int b;
    AdditionTask(int i, int j)
    {
        this.a = i;
        this.b = j;
    }

    @Override
    public void run()
    {
        System.out.println("Performing the task on: " + new Date());
        System.out.println("The sum is: " + (a+b));
    }    
}
public class Demo
{
    public static void main(String[] args) throws InterruptedException
    {
        AdditionTask at = new AdditionTask(10, 20);

        long delay  = 1;
        long repeatPeriod = 2;

        ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor();
        exe.scheduleAtFixedRate(at, delay, repeatPeriod, TimeUnit.SECONDS);
    }
}

执行任务时间:2021 年 8 月 04 日星期三 19:33:21 IST 执行任务时间:2021 年 8 月 04 日星期三 19:33:23 IST 执行任务时间:2021 年 8 月 04 日星期三 19:33:25 IST 执行任务时间:2021 年 8 月 04 日星期三 19:33:27 IST

它们与 Timer 类非常相似,但有一些显著的区别。以下几点总结了计时器和执行器服务之间的主要区别。

  • 计时器受系统时钟变化的影响,但 ExecutorService 不受影响。
  • 计时器只能有一个线程,但是执行器服务可以使用多个线程。
  • 计时器线程中的运行时异常将取消所有剩余的任务。异常不会影响执行器服务。

摘要

定时器任务通过使用定时器类进行调度。Timer 类包含用于调度任务的重载 schedule()方法。我们也可以使用 schedule()方法在特定的时间间隔后重复任务。本教程中讨论的重载 schedule()方法总结如下。

  • 计划(TimerTask t,长延迟)用于在延迟后计划任务。
  • 计划(时间任务,日期 d)用于将任务安排在特定的日期和时间。
  • 计划(时间任务、长延迟、长重复周期)用于在初始延迟后计划可重复的任务。重复周期结束后,任务会重复执行。
  • 计划(时间任务 t,日期 d,长重复周期)将在重复周期后重复任务,但任务将在提到的日期开始。