首先,如果要执行一些简单的定时器任务,无须做复杂的控制,也无须保存状态,那么可以考虑使用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方法中,当任务链表中没有任务时,永久休眠,添加新的任务时,唤醒线程,线程中判断该任务是否超时,如果不超时计算出超时时间,并且休眠,如果超时,则删除该任务。
以上程序可以继续扩展,可以将超时时间放到任务实体中,做到完全控制任务的运行时间