/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.concurrent.timer; import java.io.File; import java.io.Serializable; import java.util.Date; import jef.common.DateSpan; import jef.common.log.LogUtil; import jef.tools.Assert; import jef.tools.IOUtils; import jef.tools.StringUtils; /** * 由于Trigger方法中涉及重新安排任务的时间,造成需要存取TimerTask的state属性, * 为此不得不将JDK的Timer和TimerTask抄了一份到这里来。 * @author jiyi */ public abstract class Job extends TimerTask implements Serializable { /** * 描述一次执行的情况 * @author Administrator * */ public static class ExecuteInfo implements Serializable{ private static final long serialVersionUID = -346868877252111768L; private boolean force; private DateSpan firetime; private String exception; public ExecuteInfo(DateSpan loadObject) { this.firetime=loadObject; } /** * 获得上次运行的执行时间 * @return */ public DateSpan getFiretime() { return firetime; } public String getException() { return exception; } public boolean isForce() { return force; } } /** * */ private static final long serialVersionUID = 7587521178032575229L; protected ExecuteInfo lastFire;//上次执行记录 /** * 如果是调用trigger方法强制执行的,此标志会设置为true; */ protected boolean isForceFire=false; protected Date currentStartTime; private String name; protected Timer timer; private JobState state=JobState.NOT_SCHEDULED; /** * 延迟时间(第一次运行前)单位ms * @return */ protected abstract long getDelay(); /** * 得到间隔时间,单位ms * 如果返回0,表示此任务只运行一次。 */ protected abstract long getPeriod(); /** * 执行的内容 */ protected abstract void execute(); /** * 返回名称 * @return */ public String getName() { return name; } /** * 设置名称 * @param name */ public void setName(String name) { Assert.equals(JobState.NOT_SCHEDULED, state, "The trigger's cann't be renamed after SCHEDULED."); this.name = name; initTime(name); } public enum JobState{ NOT_SCHEDULED,//未安排,未运行 RUNNING,//正在运行 SCHEDULED,//已安排 } //季怡关于Java的Timer 和 TimerTask的代码阅读要点 //1 一个Timer中可以安排多个Task,但每个Timer是一个线程运行,所以一个Task长时间运行会堵塞整个Timer,造成后续任务无法运行。 //因此设计还是让每个Job一个Timer(拥有独立的计时线程) //2 Timertask中的scheduledExecutionTime可以获得上次运行时间(开始时间而非任务结束时间,实际上是根据已经安排的下次时间倒推计算的) //3 timerTask的Cancel方法可以取消任务或者安排。(返回True即取消有效,否则返回False) //4 要想重新安排任务,必须先Cancel这一个任务。 //5 scheduleAtFixedRate和普通schedule区别在于其会保证在一定时间内的运行次数。但是还在Timer的同一个单线程中运行, //一旦进度延后其会尝试连续运行多次来追赶安排的进度。(个人感觉意义不大) private final void initTime(String name) { if(stateSaved()){ String fileName=this.getClass().getSimpleName()+"_"+name+".last"; File lastStatus=new File(fileName); if(lastStatus.exists()){ Object obj=IOUtils.loadObject(lastStatus); if(obj instanceof DateSpan){ lastFire=new ExecuteInfo((DateSpan)obj); }else if(obj instanceof ExecuteInfo){ lastFire=(ExecuteInfo)obj; } } } } /** * 表示此类是否具有时间持久化特性。 * @return */ protected boolean stateSaved() { return true; } /** * 计时器开始运作 */ public final void startTimer(){ if(timer==null){ timer=new Timer(true); timer.start(); long d=getDelay(); if(lastFire!=null){//如果上次运行时间能够获得 long wait=getPeriod()-(System.currentTimeMillis()-lastFire.getFiretime().getStart().getTime()); if(wait>d)d=wait; } timer.schedule(this, d,getPeriod()); state=JobState.SCHEDULED; } } /** * 停止计时器,包括计时器线程等所有内容 */ public final void stopTimer(){ if(timer!=null){ this.cancel(); timer.cancel(); timer=null; state=JobState.NOT_SCHEDULED; } } /** * 如果当前Job正在运行,得到Job的开始时间。否则返回null * @return */ public Date getCurrentStartTime() { return currentStartTime; } /** * 获得状态 */ public JobState getState(){ return state; } /** * 立即触发任务,相应的下次运行时间会重新调整 */ public void trigger(){ if(timer==null){ LogUtil.show(this.getName()+" no timer, will execute in current thread directly."); isForceFire=true; this.run(); isForceFire=false; }else{ this.cancel(); timer.purge();//移除该任务 synchronized(lock) {//强行回复状态(修补XX膜?) super.state=TimerTask.VIRGIN; } isForceFire=true; timer.schedule(this, 200, getPeriod()); isForceFire=false; } } /** * 在计时器线程中被调用,当有多个线程调用此方法时会被阻塞 */ public synchronized void run() { state=JobState.RUNNING; currentStartTime=new Date(); String exception=null; try{ execute(); }catch(Throwable t){ exception = StringUtils.exceptionSummary(t); } //将上次运行时间保存到本地 if(stateSaved()){ String fileName=this.getClass().getSimpleName()+"_"+getName()+".last"; File statusFile=new File(fileName); lastFire=new ExecuteInfo(new DateSpan(currentStartTime,new Date())); lastFire.force=isForceFire; lastFire.exception=exception; IOUtils.saveObject(lastFire, statusFile);//每次运行结束时,将时间持久化到磁盘上,下次构造时自动读取 } currentStartTime=null; state=JobState.SCHEDULED; } /** * 得到上次运行的时间段,没有运行结束过返回null * @return */ public DateSpan getLastFire() { return lastFire.getFiretime(); } /** * 得到上次运行的详细信息 * @return */ public ExecuteInfo getLastFireInfo() { return lastFire; } }