为了找出问题,我采用了两种方式来测试我的定时器
第一种方式,代码如下 :import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;class MyTask extends TimerTask { public void run() {
System.out.println("现在的时间是:"
+ (new SimpleDateFormat("yy-MM-dd HH:mm")).format(new Date())); }
}public class AutoDo {
private long timestemp; public AutoDo(int seconds) {
timestemp = seconds * 1000;
} public void back() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 1);
calendar.set(Calendar.MINUTE, 10);
calendar.set(Calendar.SECOND, 0);
// 如果要参数boolean,则不执行,为什么?
Timer timer = new Timer();
// 从指定的时间开始,周期性地执行任务task
timer.schedule(new MyTask(), calendar.getTime(), timestemp);
// timer.cancel();
} public static void main(String[] args) {
AutoDo auto = new AutoDo(2);
System.out.println("About to schedule task.");
auto.back();
}
}第二种方式,我采用监听器,代码如下:
BackTimerTask 类:public class BackTimerTask extends TimerTask { // 在run()方法中调用对数据库的备份操作
public void run() {
this.doit();
} private void doit() {
System.out.println("web 监听器结合定时器的应用每次跳了两下。。");
}
}==================================================================================
BackListen 监听器:import java.util.Calendar;
import java.util.Timer;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;// 在初始化方法中启动定时器
// 在销毁方法中终止定时器
public class BackListen implements ServletContextListener { private Timer timer; // 初始化方法
public void contextInitialized(ServletContextEvent arg0) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 1);
calendar.set(Calendar.MINUTE, 27);
calendar.set(Calendar.SECOND, 0);
long period = 2 * 1000;
timer = new Timer();
BackTimerTask task = new BackTimerTask();
// 创建一个定时器
// 从firstTime这个时间开始周期性执行固定延迟period的任务task
// 在这里从23:40分开始执行,延迟2秒执行一次
System.out.println("开始时间-->" + calendar.getTime());
timer.schedule(task, calendar.getTime(), period);
} // 销毁方法
public void contextDestroyed(ServletContextEvent arg0) {
if (timer != null) {
timer.cancel();
}
}
}========================================================================================
web.xml文件中监听器的配置:<!-- 监听器配置 -->
<listener>
<listener-class>
org.qpyong.emp.listen.BackListen
</listener-class>
</listener>对于第二种方式,每次要执行task时,“web 监听器结合定时器的应用每次跳了两下。。”这语句都被执行了两次!但是采用第一种方式却是正常的。我纳闷了。请各位帮我看看,问题出在哪。

解决方案 »

  1.   

    因为ServletContextListener:监听对ServletContext属性的操作,如初始化,增加、删除、修改操作,这样的话很可能是你的程序中涉及到了对ServletContext的初始化就会调用一次,增加属性又会调用一次,看下你的servlet中是否是这样的!
      

  2.   

    谢谢楼上意见!但是你看我的源代码(BackListen ),除了初始化外,我没有其他任务的如你说的增加属性啊。
      

  3.   

    哦,可能是我没说清楚,我指的是servlet中对ServletContext对象的初始化等操作,因为监听器监听的是对该对象的操作,因为你没有帖你servlet中的内容,所以我也只是猜测在servlet中你做了多次的ServletContext对象的操作,从而被监听器监听到了
      

  4.   

    谢谢你的答复!我只用到监听器,没有用到servlet来启动定时器。请看BackListen 这个监听器。而在web.xml文件中的配置是没有问题的。
      

  5.   

    将你的BackTimerTask改造如下: class BackTimerTask extends TimerTask {
     private static int i=1;
    // 在run()方法中调用对数据库的备份操作
    public void run() {
    this.doit();
    } private void doit() {
    System.out.println("web 监听器结合定时器的应用每次跳了两下。。"+i+" "+Calendar.getInstance().getTime());
    this.i++;
    }
    }发现启动应用后输出如下:
    信息: Starting Servlet Engine: Apache Tomcat/6.0.13
    开始时间-->Sat Oct 11 01:27:00 CST 2008
    web 监听器结合定时器的应用每次跳了两下。。1 Sat Oct 11 19:21:21 CST 2008
    2008-10-11 19:21:21 org.apache.coyote.http11.Http11Protocol start
    信息: Starting Coyote HTTP/1.1 on http-8080
    2008-10-11 19:21:21 org.apache.jk.common.ChannelSocket init
    信息: JK: ajp13 listening on /0.0.0.0:8009
    2008-10-11 19:21:21 org.apache.jk.server.JkMain start
    信息: Jk running ID=0 time=0/41  config=null
    2008-10-11 19:21:21 org.apache.catalina.startup.Catalina start
    信息: Server startup in 1497 ms
    web 监听器结合定时器的应用每次跳了两下。。2 Sat Oct 11 19:21:23 CST 2008
    web 监听器结合定时器的应用每次跳了两下。。3 Sat Oct 11 19:21:25 CST 2008
    web 监听器结合定时器的应用每次跳了两下。。4 Sat Oct 11 19:21:27 CST 2008
    监听器装入容器时会执行一次,不知道你说的跳两次是不是这样的
      

  6.   

    昨天下午我也用着跟你一样的思路测试了代码。
    BackTimerTask代码如下:// 在run()方法中调用对数据库的备份操作
    public void run() {
    this.doit();
    // BakUpDao dao = new BakUpDao();
    // dao.backUp();
    } private void doit() {
    System.out.println("开始:"
    + (new SimpleDateFormat("yy-mm-dd HH:mm:ss"))
    .format(new Date()));
    System.out.println("web 监听器结合定时器的应用每次跳了两下。。");
    System.out.println("结束:"
    + (new SimpleDateFormat("yy-mm-dd HH:mm:ss"))
    .format(new Date()));
    }在MyEclipse后台输出的结果是:
    -----------------------
    开始:08-51-11 15:51:26
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-51-11 15:51:26
    开始:08-51-11 15:51:31
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-51-11 15:51:31
    开始:08-51-11 15:51:46
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-51-11 15:51:46
    ------------------------
    就是说问题还是存在。
    仔细检查下整个应用程序的Servlet,发现另外一个开发者居然多次使用了ServletContext对象。
    干脆把这个监听器"移植"到一个干净的Web环境。
    通过新建一个新的Web project,一经测试,果然得到了预期效果:开始:08-21-12 01:21:35
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-21-12 01:21:35
    开始:08-21-12 01:21:40
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-21-12 01:21:40
    开始:08-21-12 01:21:45
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-21-12 01:21:45
    开始:08-21-12 01:21:50
    web 监听器结合定时器的应用每次跳了两下。。
    结束:08-21-12 01:21:50你会发现,在“开始”与“结束”时刻,时间都非常准确的间隔了5秒(我后来把时间延迟改为5秒,因为我开始怀疑是时间延迟太短而导致任务执行时占用下次任务执行的时间。)
    截止到现在,问题终于可以得到完美解决了。
    也非常感谢zhongwenly你对我帖子的关注与帮助!
      

  7.   

    不对,我在新建的Web project中测试定时器,它会按正常的固定的延迟周期性地执行任务。
    但是当我把原来那个(Empoyee)project 的所有关于引用ServletContext对象的servlet注释掉之后,再重新部署、启动服务器,
    结果还是原来的那样:还是每次到执行任务时刻时,都执行了两次任务!!!
    好郁闷啊.....
      

  8.   

    不对,我在新建的Web project中测试定时器,它会按正常的固定的延迟周期性地执行任务。 
    但是当我把原来那个(Empoyee)project 的所有关于引用ServletContext对象的servlet注释掉之后,再重新部署、启动服务器, 
    结果还是原来的那样:还是每次到执行任务时刻时,都执行了两次任务!!! 
    好郁闷啊.....
      

  9.   

    我遇到同样的问题
    是因为在配置虚拟目录时tomcat/conf/server.xml里配置了一个context应用