Java计时器类:如果其中一个任务抛出异常,计时器任务将停止执行。

13
new Timer().scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
       System.out.println("run");
       throw new SomeRandomException();
    }
 }, 1000, 1000);

输出:运行(抛出异常)

问题如下:我需要一个定时任务来检查数据库(或其他内容)中的特定条件。它一直在正常工作,但有时数据库(或其他内容)返回一些错误,导致异常被抛出并且计时器崩溃,此后没有一个单独的定时器任务会再次执行。是否有一种定时器实现可以在run()方法抛出异常后继续工作。

我可以

new Timer().scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        try {
            System.out.println("run");
            throw new SomeRandomException();
        } catch (Exception e) {
            System.out.println("dummy catch");
        }
    }
}, 1000, 1000);

但这个方法似乎有些无力。

另一种选择是编写自己的Timer类实现,吞咽run方法的异常(这也不太对)。


你还需要捕获 Error,否则它们会悄悄地终止你的任务。 - Peter Lawrey
日志和异常捕获在运行中有什么问题? - Renetik
3个回答

10

使用ScheduledExecutorService。它与Timer扮演相同的角色,但修复了其缺陷(例如您遇到的问题)。


2
不幸的是,“如果任务的任何执行遇到异常,则抑制后续执行。”- 来自http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate(java.lang.Runnable,long,long,java.util.concurrent.TimeUnit)。 - Kiril Kirilov
1
是的,这个特定的任务将停止运行,但它不会使执行器崩溃,其他任务仍然按计划进行。但我同意您仍然必须捕获可能发生的任何异常,并且不能导致任务的调度被中止。您可能希望创建一个可重用的CatchAllRunnable类。 - JB Nizet

0

使用 ExecutorService; 可以处理编译时和运行时异常。

从Java ExecutorService任务处理异常
如何在Callable中处理异常

ThreadPoolExecutor.java

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

0
说实话,我认为在timertask运行中捕获和记录异常没有任何问题,特别是如果你能够捕获所有异常并正确地记录它们,这样它们就不会丢失。
fun Timer.schedule(delay: Long = 0, function: () -> Unit): TimerTask {
    val task = object : TimerTask() {
        override fun run() {
            try {
                function()
            } catch (e: Throwable) {
                logError(e)
            }
        }
    }
    schedule(task, delay)
    return task
}

另一个选择是捕获重新抛出并重新创建计时器实例,但我不知道这种情况的用例,在Android上,我可能会像这样实现它:

private var timer = Timer()

schedule(delay: Long = 0, function: () -> Unit): TimerTask {
        val task = object : TimerTask() {
            override fun run() {
                try {
                    function()
                } catch (e: Throwable) {
                    later { timer = Timer() }
                    throw e
                }
            }
        }
        timer.schedule(task, delay)
        return task
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接