java 内置定时器实现定时任务和自定义定时任务

时间:2021-05-01 07:45:40

首先,如果要执行一些简单的定时器任务,无须做复杂的控制,也无须保存状态,那么可以考虑使用JDK 入门级的定期器Timer来执行重复任务。

JDK中,定时器任务的执行需要两个基本的类:
    java.util.Timer;
    java.util.TimerTask;

要运行一个定时任务,最基本的步骤如下:
1、建立一个要执行的任务TimerTask,TimerTask是一个抽象类,由 Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()----计时器任务要执行的操作。因此,每个具体的任务类都必须继承TimerTask类,并且重写run()方法。
2、创建一个Timer实例,通过Timer提供的schedule()方法,将 TimerTask加入到定时器Timer中,同时设置执行的规则即可。
 
当程序执行了Timer初始化代码后,Timer定时任务就会按照设置去执行。
 
Timer中的schedule()方法是有多种重载格式的,以适应不同的情况。该方法的格式如下:
 void schedule(TimerTask task, Date time)
          安排在指定的时间执行指定的任务。
 void schedule(TimerTask task, Date firstTime, long period)
          安排指定的任务在指定的时间开始进行重复的固定延迟执行。
 void schedule(TimerTask task, long delay)
          安排在指定延迟后执行指定的任务。
 void schedule(TimerTask task, long delay, long period)
          安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

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

public class TimerTest {

public static void main(String[] args) {

Timer timer = new Timer();
timer.schedule(new MyTask(), 1000, 2000);// 在1秒后执行此任务,每次间隔2秒,如果传递一个Data参数,就可以在某个固定的时间执行这个任务.

Scanner sc = new Scanner(System.in);
System.out.println("输入任何字符串停止任务:" + sc.next());
timer.cancel();
}

/**
* 定时任务实体
* @author Administrator
*
*/
static class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("run");
}
}

}

下面给出一个复杂点的例子,其中告诉大家怎么退出单个TimerTask,怎么退出所有Task

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

public class TimerTest1 {
public static void main(String[] args) {
Timer timer = new Timer();
MyTask myTask1 = new MyTask();
myTask1.setInfo("task-1");
MyTask myTask2 = new MyTask();
myTask2.setInfo("task-2");

timer.schedule(myTask1, 1000, 2000);
timer.scheduleAtFixedRate(myTask2, 2000, 3000);

while (true) {
try {
Scanner sc = new Scanner(System.in);
String state = sc.next();
//当接收到的字符串时 cancel_1 时,停止 myTask1 任务
if(state.equals("cancel_1")){
myTask1.cancel();
}
//当接收到的字符串时 cancel_2 时,停止 myTask2 任务
else if(state.equals("cancel_2")){
myTask2.cancel();

//当接收到的字符串时 cancel_all 时,停止 所有任务
}else if(state.equals("cancel_all")){
timer.cancel();
break;
}
//当接受到开头是 task_1 的字符串,替换 myTask1实体
if(state.startsWith("task_1")){
myTask1.setInfo(state);
//当接受到开头是 task_1 的字符串,替换 myTask2实体
}else if(state.startsWith("task_2")){
myTask2.setInfo(state);
}

} catch (Exception e) {
e.printStackTrace();
}
}
}

/**
* 定时任务实体
* @author Administrator
*
*/
static class MyTask extends TimerTask {
String info = "";

public void run() {
System.out.println(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
}

但是我们在应用场景中会遇到每一个任务的执行时间都不相同,并且每一个任务只执行一次,如果按照上面的做法,就是每次生成一个TimerTask放到Timer中执行,这样太耗费资源了,我们需要实现一个线程来监听所有的任务。

public class TimerTest2 extends Thread {

private static long TIME_OUT = 10000;// 每一个任务在定时器中的超时时间
// 任务列表,线程安全
private static List<MyTask> myTasks = Collections
.synchronizedList(new ArrayList<MyTask>());

public void run() {
while (true) {
// 同步
synchronized (myTasks) {
if (myTasks.size() > 0) {

MyTask myTask = myTasks.get(0);
long flagTime = TIME_OUT
- (new Date().getTime() - myTask.getDate()
.getTime());
// 获取第一个任务,并且判断该任务是否超时,如果是,删除该任务
if (flagTime < 0) {
myTasks.remove(myTask);
} else {
try {
// 如果该任务没有超时,继续等待时间
myTasks.wait(flagTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
try {
myTasks.wait();// 如果列表为空,那么就一直休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

/**
* 添加新的任务
*
* @param info
*/
public void addTask(String info) {

// 创建新的任务
MyTask myTask = new MyTask();
myTask.setDate(new Date());
myTask.setInfo(info);
// 同步
synchronized (myTasks) {
// 将任务添加到任务列表中,并且唤醒线程
myTasks.add(myTask);
myTasks.notify();
}
}


/**
* 检查该任务是否存在
* @param info
* @return
*/
public boolean checkTask(String info) {
synchronized (myTasks) {
//遍历任务列表,判断该任务是否存在
for (MyTask myTask : myTasks) {
if (myTask.getInfo().equals(info)) {
return true;
}
}
return false;
}
}


public static void main(String[] args) {
TimerTest2 timerTest2 = new TimerTest2();
timerTest2.start();//启动线程

timerTest2.addTask("task");

try {
Thread.sleep(5000);
System.out.println("当前过了5秒,任务 task 是否存在 :" + timerTest2.checkTask("task"));

Thread.sleep(6000);
System.out.println("当前过了11秒,任务 task 是否存在 :" + timerTest2.checkTask("task"));


} catch (InterruptedException e) {
e.printStackTrace();
}

}



/**
* 任务实体类
* @author Administrator
*
*/
static class MyTask {
String info;
Date date;//任务实体生成时间

public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}

}

}

从以上程序可以看出,每个任务(MyTask) 都放置了一个date(生成实体对象的时间),然后设置每一个任务的超时时间为10秒。任务是以链表的形式存放的,在run方法中,当任务链表中没有任务时,永久休眠,添加新的任务时,唤醒线程,线程中判断该任务是否超时,如果不超时计算出超时时间,并且休眠,如果超时,则删除该任务。

以上程序可以继续扩展,可以将超时时间放到任务实体中,做到完全控制任务的运行时间