【Java基础】线程池中异常丢失现象分析

in 网站建设
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

背景

最近在开发一个证书管理的需求,在Spring容器启动之后需要在线程中异步定时更新SSL证书。于是我使用ScheduledThreadPoolExecutor线程池来定时更新线程。

  1. 在调试的过程中发现有丢失日志的现象,线程执行到某一步之后就没有再执行后面的代码了。定时任务只执行了一次。
  2. 跟踪代码发现代码在执行到了某一步之后,突然消失,没有任何异常抛出的现象。但是分析代码在消失的位置应该出现空指针异常的,那么为什么异常会突然消失呢?

分析源码

首先我们需要对IDEA进行设置,是它能够进入jdk里的代码,如下图。
image.png
如果我们勾选了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捕获之前进行捕获处理即可。

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看