最近在开发一个证书管理的需求,在Spring容器启动之后需要在线程中异步定时更新SSL证书。于是我使用ScheduledThreadPoolExecutor线程池来定时更新线程。
首先我们需要对IDEA进行设置,是它能够进入jdk里的代码,如下图。
如果我们勾选了Do not step into the classes,调试时,不会进入下面类中,而是直接跳过了。这就是为什么为什么我之前调试过程中发现代码执行到了某一步就突然消失了。因为后面要执行的代码都在图中下面的类中。
其次执行如下代码并点击下一步。
class Solution { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(1); scheduledThreadPool.scheduleAtFixedRate(() -> { throw new RuntimeException(); }, 0, 10, TimeUnit.SECONDS); } }
抛出异常之后代码进入了FutureTask里的runAndReset()方法里。
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) { 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; }
我们可以从上面的源码中确定抛出的异常被捕获了,并通过setException()方法处理
protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }
由此我们可以确定抛出的异常被放在了outcome变量里面。
异常是一步一步向上抛出直到被runAndReset里面的try-catch语句块捕获为止。所以如果我们需要在异常中处理异常,那么我们需要在被runAndSet里的try-catch捕获之前进行捕获处理即可。