定时任务里肯定可以查到 Timer ,关于 Timer 的介绍,有很多很详细的,我这里打算对该类进行演进,先从这个最简单的定时工具讲起,逐步实现定时任务的功能,周期性任务计划,多任务周期性计划。
起因是因为我现在的项目上有周期性日程计划,但是计划了时间却不能准确进行,于是我打算自己试试写一个,而且由于我对 Timer 和 Thread 不是很熟悉,所以打算一步步演进项目,先从小的步骤开始进行,计划步骤如下:
第一步:实现1秒执行一次,总共执行三次
第二步:设置时间,到期执行
第三步:设置三个时间,三个任务,到期都要执行,产生不同的log文件,然后周期化,先设置成每天,然后每周
第四步:如果有成千上万个任务,如果降低线程的使用率,目前的想法是给执行任务的先后任务排序,最先执行的,先计时
第五步:网页界面,按钮:开始任务,任务临时退出(任务还继续,但是系统需要关闭一次,希望下次启动系统时继续任务,很可能出现启动系统时,本该执行的任务错过了执行时间,记录日志)
于是本篇先实现前两步。
第一步很简单代码如下:
public class TimerTest03 {
Timer timer;
public TimerTest03(){
timer = new Timer();
timer.schedule(new TimerTaskTest03(), 1000, 2000);
}
public static void main(String[] args) {
new TimerTest03();
}
public class TimerTaskTest03 extends TimerTask{
int count=0;
@Override
public void run() {
Date date = new Date(this.scheduledExecutionTime());
System.out.println("本次执行该线程的时间为:" + date);
count++;
if(count==3) {
this.cancel();
}
}
}
}
第二步也不难,只需要知道:schedule(TimerTask task, Date time)-安排在指定的时间执行指定的任务。
public class TimeScheduleTask {
Timer timer;
public TimeScheduleTask(Date scheduledTime) {
System.out.println("指定时间time=" + scheduledTime);
timer = new Timer();
timer.schedule(new TimerTaskTest(), scheduledTime);
}
public class TimerTaskTest extends TimerTask {
@Override
public void run() {
System.out.println("指定时间执行线程任务...");
}
}
public static void main(String[] args) {
// 创建 Calendar 对象
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d H:m:s");
// 指定一个日期
Date date;
try {
date = dateFormat.parse("2019-8-30 09:25:16");
// 对 calendar 设置为 date 所定的日期
calendar.setTime(date);
new TimeScheduleTask(calendar.getTime());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Timer 是基于绝对时间,对系统时间特别敏感,在现在的项目里都是对日期有要求的,所以绝对时间是没有问题的,但是 Timer 内部是单一线程的,发生异常就会终止全部任务,于是给出了一个比较优秀的解决办法,就是使用 ScheduledThreadPoolExecutor ,这样就可以支持多任务了。
ScheduledExecutorService scheduExec=Executors.newScheduledThreadPool(2);
我们可以看看官方的解释
public static ExecutorService newFixedThreadPool(int nThreads)
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.
Parameters:
nThreads - the number of threads in the pool
Returns:
the newly created thread pool
Throws:
IllegalArgumentException - if nThreads <= 0
上面的代码只是演示,我们需要提取我们需要的部分,我们希望实现的功能有,指定一个开始日期,这个日期是实际的日期,也就是服务器的时间,然后开始一个任务,这个任务是周期性的。为了不再是控制台打印,我将任务的内容改为记录日志,这样可以方便记录时间和产生文字内容。
任务代码:
public class ScheduleTask extends Thread {
public void run() {
CaeLogger.WriteLog("ScheduleTask", "goo");
}
}
周期性任务:
public class ScheduledExecutorTest {
private ScheduledExecutorService scheduExec;
private ScheduleTask myTask;
ScheduledExecutorTest(long num) {
this.scheduExec = Executors.newScheduledThreadPool(1);
myTask = new ScheduleTask();
scheduExec.scheduleAtFixedRate(myTask, 0, num, TimeUnit.MINUTES);
}
public void startTask() {
myTask.start();
}
public void endTask() {
// 关闭具体任务线程
myTask.interrupt();
// 关闭周期计划
scheduExec.shutdown();
}
}
为了方便我做测试,我把改任务的周期设置为固定值,而且是每隔一分钟执行一次。下面是开始时间和结束时间主类:
public class TimeScheduleTask {
Timer timer;
Timer timerEnd;
ScheduledExecutorTest myTask;
TimerTaskTest1 timerF;
public TimeScheduleTask(Date startTime, Date endTime) {
System.out.println("指定时间time=" + startTime);
CaeLogger.WriteLog("startTie", "XX");
timer = new Timer();
timerEnd = new Timer();
timerF = new TimerTaskTest1();
timer.schedule(timerF, startTime);
timerEnd.schedule(new TimerTaskTest2(), endTime);
}
public class TimerTaskTest1 extends TimerTask {
@Override
public void run() {
myTask = new ScheduledExecutorTest(1);
myTask.startTask();
}
}
public class TimerTaskTest2 extends TimerTask {
@Override
public void run() {
if (myTask != null) {
myTask.endTask();
timerF.cancel();
this.cancel();
}
}
}
public static void main(String[] args) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-M-d H:m:s");
// 指定一个日期
Date date;
Date date1;
try {
date = dateFormat.parse("2019-8-30 14:54:00");
date1 = dateFormat.parse("2019-8-30 14:59:00");
new TimeScheduleTask(date, date1);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}