问题
如果定时任务异常,会发生什么?后面还会继续执行吗?
结论
不会。
源码分析
为什么不会继续执行?
核心代码如下。核心作用是,调用线程循环执行业务线程。
java.util.TimerThread#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) {
}
}
}
核心代码,看这行:
//如果时间到了,就执行任务线程
task.run(); //
问题就在这里,这里会执行业务线程。
如果业务线程异常了,会导致java.util.TimerThread#mainLoop方法里的while循环退出了,因为while循环里的代码异常了。
while循环退出之后,本质其实是调度线程结束了,定时器Timer也就不能继续处理业务线程了。
而,正常情况下,调度线程其实是应该一直循环执行业务线程。
demo
通过模拟异常,来模拟测试。
具体来说是:
- 模拟业务线程异常
异常的时候,会抛出异常。
- 业务线程抛出异常,会导致调度线程退出while循环
其实本质是调度线程结束了,就不能继续处理业务线程了。
测试代码
@Test
public void givenUsingTimer_whenSchedulingRepeatedTask_thenCorrect2()
throws InterruptedException, IOException { //重复执行:模拟异常
//创建调度器
final TimerTask repeatedTask = new TimerTask() {
@Override
public void run() {
LOG.info("Task performed on " + new Date());
int i = 1 / 0; //模拟异常
}
};
//执行任务线程
final Timer timer = new Timer("Timer");
final long delay = 3000L;
final long period = 1000L;
try {
timer.scheduleAtFixedRate(repeatedTask, delay, period);
} catch (Exception e) {
LOG.error("异常",e);
}
//
System.in.read();
// Thread.sleep(delay * 2);
// timer.cancel();
}
测试结果
11:15:57.417 [Timer] INFO c.b.t.JavaTimerLongRunningUnitTest - Task performed on Fri Aug 19 11:15:57 CST 2022
Exception in thread "Timer" java.lang.ArithmeticException: / by zero
at com.baeldung.timer.JavaTimerLongRunningUnitTest$3.run(JavaTimerLongRunningUnitTest.java:79)
at java.base/java.util.TimerThread.mainLoop(Timer.java:556)
at java.base/java.util.TimerThread.run(Timer.java:506)
总结
业务线程异常之后,会导致调度线程退出while循环
退出while循环之后,会执行到这里: java.util.TimerThread#run
public void run() {
try {
mainLoop(); //业务线程异常之后,会导致调度线程退出while循环
} finally { //然后,继续往下执行
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
打了断点如下,已经执行到finally代码,说明while循环已经退出了