当前位置:   article > 正文

修改系统时间后,导致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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/83591
推荐阅读
相关标签
  

闽ICP备14008679号