修改系统时间后,导致Spring定时任务自动挂起

今天在系统定时任务中发现了一个问题,一旦修改系统时间,把系统时间调到当前时间之后(即大于当前时间T1)T2,Timer线程正常执行;如果再将系统时间修改到当前时间之前T3(即T3小于T2),那么Timer线程就会挂起,或者假死,此时不会再执行定时任务,好像线程已经死掉一样。

   

我们可以从Timer的实现源码中找下原因,从schedule方法进去,一直追踪调试下去,可以找到:

public void run() {
   try {
       mainLoop();
   } finally {
       // Someone killed this Thread, behave as if Timer cancelled
       synchronized(queue) {
           newTasksMayBeScheduled = false;
           queue.clear();  // Eliminate obsolete references
       }
   }
}

Timer的实现原理原来就是在一个线程中执行mainLoop()函数,再看下mainLoop()函数源码:

/**
* The main timer loop.  (See class comment.)
*/
private void mainLoop() {
   while (true) {
       try {
           TimerTask task;
           boolean taskFired;
           synchronized(queue) {
               // Wait for queue to become non-empty
               while (queue.isEmpty() && newTasksMayBeScheduled)
                   queue.wait();
               if (queue.isEmpty())
                   break; // Queue is empty and will forever remain; die

               // Queue nonempty; look at first evt and do the right thing
               long currentTime, executionTime;
               task = queue.getMin();
               synchronized(task.lock) {
                   if (task.state == TimerTask.CANCELLED) {
                       queue.removeMin();
                       continue;  // No action required, poll queue again
                   }
                   currentTime = System.currentTimeMillis();
                   executionTime = task.nextExecutionTime;
                   if (taskFired = (executionTime<=currentTime)) {
                       if (task.period == 0) { // Non-repeating, remove
                           queue.removeMin();
                           task.state = TimerTask.EXECUTED;
                       } else { // Repeating task, reschedule
                           queue.rescheduleMin(
                             task.period<0 ? currentTime  - task.period
                                           : executionTime + task.period);
                       }
                   }
               }
               if (!taskFired) // Task hasn't yet fired; wait
                   queue.wait(executionTime - currentTime);
           }
           if (taskFired)  // Task fired; run it, holding no locks
               task.run();
       } catch(InterruptedException e) {
       }
   }
}

代码中出现了while(true),说明一直在重复地执行,可以猜测我们的任务代码就是放在while(true)里面执行的。确定是这样,我们的任务被放在了一个queue的队列中,mainLoop方法就是不断地执行队列里面所有的任务。

 

//获取当前系统时间
currentTime = System.currentTimeMillis();
//下一次执行任务的时间
executionTime = task.nextExecutionTime;

taskFired = (executionTime<=currentTime)

 

如果下一次执行任务的时间小于等于当前时间,证明执行任务的时间已经到了,向下执行任务,如果不是,即taskFired=false,那么就等待,等待的时间为executionTime - currentTime:

if (!taskFired) // Task hasn't yet fired; wait
      queue.wait(executionTime - currentTime);

 

如果我们把系统时间调整为未来的时间,那么executionTime<=currentTime肯定为true,执行下次任务,程序运行正常;

如果我们再次调整系统时间为之前的时间,那么此时executionTime<=currentTime肯定为false,那么就会执行代码queue.wait(executionTime - currentTime),此时线程就会挂起了,至于等待的时间明显为executionTime - currentTime。

处理最好的这个方法就是重启Tomcat ,Emmmmmmm,感觉方法有点烂,暂时只有这一个,emmmmm

转载于:https://my.oschina.net/u/3792723/blog/1634343

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值