ScheduledThreadPoolExecutor定时任务会消失

ScheduledThreadPoolExecutor定时任务会消失

背景

我们通常使用Java原生ScheduledThreadPoolExecutor实现定时任务,当我们遇到定时任务不再执行情况时,一查会发现没有任何错误日志,线程还在但定时任务就是不执行,仿佛消失了,这是什么原因呢?

举例

下面例子代码,预期是每隔5秒执行一次DivideZero,但实际上只会执行1次,而且不会抛出任何异常。

public class DelayTaskException {
	
	private static final ScheduledThreadPoolExecutor schedExecutor = new ScheduledThreadPoolExecutor(5);
	
	private static class DivideZero implements Runnable{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			System.out.println("run");
			int a = 1/0;
		}
		
	}
	
	public static void main(String[] args) {
		schedExecutor.scheduleWithFixedDelay(new DivideZero(), 5, 5, TimeUnit.SECONDS);
	}
}

原因分析

ScheduledThreadPoolExecutor周期性定时任务实现原理:任务会被包裹成ScheduledFutureTask,然后丢到队列或者直接交给线程池线程首次执行,执行后设置重新执行时间,再丢回队列,之后由线程池线程取出来再执行,周而复始。

ScheduledFutureTask的run逻辑,

        /**
         * Overrides FutureTask version so as to reset/requeue if periodic.
         */
        public void run() {
            boolean periodic = isPeriodic();
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            else if (!periodic)//一次性执行
                ScheduledFutureTask.super.run();
            else if (ScheduledFutureTask.super.runAndReset()) {//周期性执行。注意下面逻辑是否执行依赖runAndReset方法返回
                //设置下次执行时间
                setNextRunTime();
                //重新把任务丢到队列
                reExecutePeriodic(outerTask);
            }
        }

ScheduledFutureTask的runAndReset逻辑,

    /**
     * Executes the computation without setting its result, and then
     * resets this future to initial state, failing to do so if the
     * computation encounters an exception or is cancelled.  This is
     * designed for use with tasks that intrinsically execute more
     * than once.
     *
     * @return {@code true} if successfully run and reset
     */
    protected boolean runAndReset() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;
        boolean ran = false;
        int s = state;
        try {
            Callable<V> c = callable;
            if (c != null && s == NEW) {
                try {
                    c.call(); // don't set result
                    ran = true;
                } catch (Throwable ex) {
                    //重点:一旦抛出Throwable(所有类型异常),ran就是false,最终方法返回也是false,不会把任务重新丢到队列。
                    setException(ex);
                }
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        return ran && s == NEW;
    }

通过上面2段源码逻辑,我们知道了一旦我们的任务抛出任何异常,任务就不会重新进入队列,就没法周期性执行了,任务消失了。

解决

解决方法很简单:run方法中捕获Throwable级别的所有异常,注意是Throwable不是Exception,只有Throwable能捕获到Exception和Error

模板代码,

run(){
		try{
			//do job
		}catch(Throwable e){
			//自定义处理
		}
	}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值