定时 / 计划功能在移动开发领域使用较多,比如 Android 技术。定时计划任务功能在 Java 中主要使用的就是 Timer 对象,他在内部使用多线程的方式进行处理,所以它和线程技术还是有非常大的关联的。
在 JDK 库中 Timer 类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务。
Timer 类的主要作用就是设置计划任务,但封装任务的类却是 TimerTask。
执行计划任务的代码要放入 TimerTask 的子类中,因为 TimerTask 是一个抽象类。
该方法的作用是在指定的日期执行一次某一任务。
创建项目5.1.1,创建类Run1.java代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run1 {
private static Timer timer = new Timer();
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-8-20 11:55:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下所示:
字符串时间:2020-8-20 11:55:00当前时间:2021-8-26 15:59:15
运行了!时间为:Fri Aug 26 15:59:15 CST 2021
任务虽然执行完了,但进程还未销毁,呈红色状态。
创建一个 Timer 就是启动一个新的线程,这个新启动的线程并不是守护线程,所以它一直在运行。
下一步将新创建的Timer改为守护线程。新建java类Run1TimerIsDaemon.java,代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run1TimerIsDaemon {
private static Timer timer = new Timer(true);
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-8-20 11:55:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下所示:
字符串时间:2020-8-20 11:55:00当前时间:2020-8-26 16:56:35
守护线程创建成功进程退出
程序运行后迅速结束当前的线程,并且TimerTask中的任务不再被运行,因为进程已经结束了。
如果执行任务的时间早于当前时间,则立即执行task任务。
示例代码如下:
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2020-8-20 11:55:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间:" + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef);
} catch (ParseException e) {
e.printStackTrace();
}
}
运行结果如下所示:
字符串时间:2020-8-20 11:55:00当前时间:2021-8-26 17:48:44
运行了!时间为:Fri Aug 26 17:48:44 CST 2021
立即执行task任务
Timer中允许有多个TimerTask任务。
创建类Run2.java,代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run2 {
private static Timer timer = new Timer();
static public class MyTask1 extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + new Date());
}
}
static public class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + new Date());
}
}
public static void main(String[] args) {
try {
MyTask1 task1 = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2022-8-27 9:00:00";
String dateString2 = "2022-8-27 9:01:00";
Date dateRef1 = sdf1.parse(dateString1);
Date dateRef2 = sdf2.parse(dateString2);
System.out.println("字符串1时间:" + dateRef1.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
System.out.println("字符串2时间:" + dateRef2.toLocaleString() + " 当前时间:" + new Date().toLocaleString());
timer.schedule(task1, dateRef1);
timer.schedule(task2, dateRef2);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下所示:
字符串1时间:2022-8-27 9:00:00 当前时间:2022-8-27 10:45:04
字符串2时间:2022-8-27 9:01:00 当前时间:2022-8-27 10:45:04
运行了!时间为:Sat Aug 27 10:45:04 CST 2022
运行了!时间为:Sat Aug 27 10:45:04 CST 2022
一个Timer中可以运行多个TimerTask任务
TimerTask 是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务有可能消耗的时间较长,则后面的任务运行的时间也会被延迟。请看下面的示例。
创建java类Run2Later.java,代码如下:
import org.omg.CORBA.PUBLIC_MEMBER;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run2Later {
private static Timer timer = new Timer();
static public class MyTask1 extends TimerTask {
@Override
public void run() {
try {
System.out.println("1 begin 运行了!时间为:" + new Date());
Thread.sleep(20000);
System.out.println("1 end 运行了!时间为:" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static public class MyTask2 extends TimerTask {
@Override
public void run() {
System.out.println("2 begin 运行了!时间为:" + new Date());
System.out.println("运行了!时间为:" + new Date());
System.out.println("2 end 运行了!时间为:" + new Date());
}
public static void main(String[] args) {
try {
MyTask1 task1 = new MyTask1();
MyTask2 task2 = new MyTask2();
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString1 = "2022-8-27 11:55:00";
String dateString2 = "2022-8-27 11:55:10";
Date dateRef1 = sdf1.parse(dateString1);
Date dateRef2 = sdf2.parse(dateString2);
System.out.println("字符串1时间:" + dateRef1.toLocaleString() + "当前时间:" + new Date().toLocaleString());
System.out.println("字符串2时间:" + dateRef2.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task1, dateRef1);
timer.schedule(task2, dateRef2);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
程序运行结果如下所示:
字符串1时间:2022-8-27 11:55:00当前时间:2022-8-27 13:59:15
字符串2时间:2022-8-27 11:55:10当前时间:2022-8-27 13:59:15
1 begin 运行了!时间为:Sat Aug 27 13:59:15 CST 2022
1 end 运行了!时间为:Sat Aug 27 13:59:35 CST 2022
2 begin 运行了!时间为:Sat Aug 27 13:59:35 CST 2022
运行了!时间为:Sat Aug 27 13:59:35 CST 2022
2 end 运行了!时间为:Sat Aug 27 13:59:35 CST 2022
任务2的运行时间被延迟
由于task1需要用时20秒执行完成,task1开始时间是13:59:15,那么将要影响task2的计划任务执行的时间,task2以此时间为基准,向后延迟20秒,task2在13:59:35执行任务。因为Task是被放入队列中的,得一个一个顺序运行。
该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一任务。
创建项目5.1.2,创建类Run.java代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run {
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了! 时间为: " + new Date());
}
}
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-27 16:20:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef, 4000);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下:
字符串时间: 2022-8-27 16:20:00当前时间:2022-8-27 16:19:53
运行了! 时间为: Sat Aug 27 16:20:00 CST 2022
运行了! 时间为: Sat Aug 27 16:20:04 CST 2022
运行了! 时间为: Sat Aug 27 16:20:08 CST 2022
运行了! 时间为: Sat Aug 27 16:20:12 CST 2022
运行了! 时间为: Sat Aug 27 16:20:16 CST 2022
运行了! 时间为: Sat Aug 27 16:20:20 CST 2022
运行了! 时间为: Sat Aug 27 16:20:24 CST 2022
运行了! 时间为: Sat Aug 27 16:20:28 CST 2022
运行了! 时间为: Sat Aug 27 16:20:32 CST 2022
运行了! 时间为: Sat Aug 27 16:20:36 CST 2022
从运行结果来看,每隔4运行一次 TimerTask 任务,并且是无限期地重复执行。
如果计划时间早于当前时间,则立即执行 task 任务。
示例代码如下:
public static void main(String[] args) {
try {
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-27 16:20:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(task, dateRef, 4000);
} catch (ParseException e) {
e.printStackTrace();
}
}
程序运行结果如下所示:
字符串时间: 2022-8-27 16:20:00当前时间:2022-8-27 16:31:39
运行了! 时间为: Sat Aug 27 16:31:39 CST 2022
运行了! 时间为: Sat Aug 27 16:31:43 CST 2022
运行了! 时间为: Sat Aug 27 16:31:47 CST 2022
运行了! 时间为: Sat Aug 27 16:31:51 CST 2022
运行了! 时间为: Sat Aug 27 16:31:55 CST 2022
运行了! 时间为: Sat Aug 27 16:31:59 CST 2022
立即执行task任务
控制台打印的结果是,程序运行后立即执行task任务。
创建Run2_1.java代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run2_1 {
static public class MyTaaskA extends TimerTask {
@Override
public void run() {
try {
System.out.println("A运行了! 时间为: " + new Date());
Thread.sleep(5000);
System.out.println("A结束了! 时间为: " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyTaaskA taaskA = new MyTaaskA();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-29 9:55:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(taaskA, dateRef, 4000);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下所示:
字符串时间: 2022-8-28 9:55:00当前时间:2022-8-28 9:54:53
A运行了! 时间为: Mon Aug 28 09:55:00 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:05 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:05 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:10 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:10 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:15 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:15 CST 2022
A结束了! 时间为: Mon Aug 28 09:55:20 CST 2022
A运行了! 时间为: Mon Aug 28 09:55:20 CST 2022
TimerTask类中的cancel()方法的作用是将自身从任务队列中清除。
创建项目Run2.java文件,代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run2 {
static public class MyTaskA extends TimerTask {
@Override
public void run() {
System.out.println("A运行了! 时间为: " + new Date());
this.cancel();
}
}
static public class MyTaskB extends TimerTask {
@Override
public void run() {
System.out.println("B运行了! 时间为: " + new Date());
}
}
public static void main(String[] args) {
try {
MyTaskA taskA = new MyTaskA();
MyTaskB taskB = new MyTaskB();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-27 9:00:00";
Timer timer = new Timer();
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(taskA, dateRef, 4000);
timer.schedule(taskB, dateRef, 4000);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行后的效果如下所示:
字符串时间: 2022-8-27 9:00:00当前时间:2022-8-28 10:41:13
A运行了! 时间为: Mon Aug 28 10:41:13 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:13 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:17 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:21 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:25 CST 2022
B运行了! 时间为: Mon Aug 28 10:41:29 CST 2022
TimerTaskA仅运行一次后被清除了
TimerTask 类的 cancel() 放啊是将子什么从任务队列中被移除,其他任务不受影响。
和 TimerTask 类中的 cancel() 方法清除自身不同,Timer 类中的 cancel() 方法的作用是将任务队列中的全部任务清空。
创建项目Run3.java文件,代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run3 {
private static Timer timer = new Timer();
static public class MyTaskA extends TimerTask {
@Override
public void run() {
System.out.println("A运行了! 时间为: " + new Date());
timer.cancel();
}
}
static public class MyTaskB extends TimerTask {
@Override
public void run() {
System.out.println("B运行了! 时间为: " + new Date());
}
}
public static void main(String[] args) {
try {
MyTaskA taskA = new MyTaskA();
MyTaskB taskB = new MyTaskB();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-27 11:00:00";
Date dateRef = sdf.parse(dateString);
System.out.println("字符串时间: " + dateRef.toLocaleString() + "当前时间:" + new Date().toLocaleString());
timer.schedule(taskA, dateRef, 4000);
timer.schedule(taskB, dateRef, 4000);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序运行结果如下所示:
字符串时间: 2022-8-27 11:00:00当前时间:2022-9-3 16:03:23
A运行了! 时间为: Sat Sep 03 16:03:23 CST 2022
进程被清空
全部任务被清除,并且进程被销毁,按钮由红色变成灰色。
Timer 类中的 cancel() 方法有时并不一定会停止执行任务之花,而是正常执行。
创建项目Run4.java文件,代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Run4 {
static int i = 0;
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("正常执行了" + i);
}
}
public static void main(String[] args) {
while (true) {
try {
i++;
Timer timer = new Timer();
MyTask task = new MyTask();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = "2022-08-27 11:00:00";
Date dateRef = sdf.parse(dateString);
timer.schedule(task, dateRef);
timer.cancel();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
程序运行后结果如下所示:
正常执行了8
正常执行了59
正常执行了71
正常执行了151
正常执行了162
正常执行了343
正常执行了495
正常执行了516
正常执行了563
正常执行了761
正常执行了1091
正常执行了1227
正常执行了1250
正常执行了1283
正常执行了1651
并没有停止执行
这是因为 Timer 类中的 cancel() 方法有时并没有争抢到 queue 锁,所以 TimerTask 类中的任务继续正常执行。
以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git