Java 定时器和定时器任务
原文:https://www.studytonight.com/java-examples/java-timer-and-timertask
Java 中的计时器用于调度任务。时间任务是我们想要执行的任务。我们可以在 TimerTask 的帮助下定义一个任务,并使用 Timer 对其进行调度。java.util.Timer
类使用后台线程来调度任务。
TimerTask
也是 java.util 的一部分,是一个实现 Runnable
接口的抽象类。我们需要覆盖 run() 方法来定义任务。
在本教程中,我们将学习如何使用计时器安排时间任务。
延迟后计划任务
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,长重复周期)将在重复周期后重复任务,但任务将在提到的日期开始。