美文网首页
70%人答不全!线程池中的一个线程异常了会被怎么处理?

70%人答不全!线程池中的一个线程异常了会被怎么处理?

作者: 小罗技术笔记 | 来源:发表于2020-04-26 23:41 被阅读0次

    线程池中的一个线程异常了会被怎么处理?

    估计很多人会是以下三点答案(me too):

    1.抛异常出来并打印在控制台上

    2.其他线程任务不受影响

    3.异常线程会被回收

    但是这里我先提前说一下以上三点不全对,下面我们来具体分析一下。

    话不多说用代码来证明

    熟悉Executors线程池(本文线程池都是指Executors)都知道 有两种提交线程的方式execute和submit方式,下面将以这两种提交方式来验证。

    贴个代码凑个数

    public static void main(String[] args) {
    ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor();
    executorService.execute(() -> run("execute方法"));
    executorService.submit(() -> run("submit方法"));
    }
    
    private static void run(String name) {
    String printStr = "【thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name+"】";
    System.out.println(printStr);
    throw new RuntimeException(printStr + ",出现异常");
    }
    
    private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() {
    ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor();
    executorService.setThreadNamePrefix("(小罗技术笔记)-");
    executorService.setCorePoolSize(5);
    executorService.setMaxPoolSize(10);
    executorService.setQueueCapacity(100);
    executorService.setKeepAliveSeconds(10);
    executorService.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executorService.initialize();
    return executorService;
    }
    

    观看执行结果,诶好奇怪

    execute执行方式抛出异常显示在控制台了。

    submit执行方式啥都没有输出。

    众所周知submit底层其实也是调用的execute,因此它也有异常只是处理方法不一样,它们的区别是:

    1、execute没有返回值。可以执行任务,但无法判断任务是否成功完成。——实现Runnable接口

    2、submit返回一个future。可以用这个future来判断任务是否成功完成。——实现Callable接口

    那怎么拿到submit中的异常呢?还是用代码来说话

    Future<?> result=executorService.submit(() -> run("submit方法"));
    try {
    result.get();
    }catch (Exception e){
    e.printStackTrace();
    }
    

    获取了一下submit方法的返回结果,发现有异常了和execute一样了,所以第一点抛异常出来并打印在控制台上不是全对的!

    到这估计大家和我一样都有一个疑问了 ,为啥execute直接抛出异常,submit没有呢?

    要知道这个答案就只能去翻源码看了

    在java.util.concurrent.ThreadPoolExecutor#runWorker中抛出了运行异常:


    在java.lang.ThreadGroup#uncaughtException进行了异常处理:


    uncaughtException是什么,我也不知道,百度了一下说这个方法是JVM调用的,在线程中只需要指定我们想要的处理方式即可


    说道这里你可能会吐槽说了这么多submit到底为啥没有直接抛出异常,到底是怎么处理了,不要慌我们再看源码找答案


    看submit源码会发现,submit中传进来的task会被封装成一个FutureTask,然后再调用execute,最后返回FutureTask。


    你会发现走的是execute方法,如下图,会发现此时的task已经是FutureTask,所以再去看一下FutureTask的run方法是咋写的。


    异常被存起来了....,再看源码是怎么实现的


    返回参数里面有一个状态state翻源码发现在report方法中同时用到outcom和state状态判断打个断点发现还真是,当state为3时抛出了异常。


    同理在找寻一下report调用位置可以很明显发现是FutureTask中get方法调用了,结合上面可以很明确了submit提交时异常被存储在线程结果信息中,当调用get方法是判断线程运行结果状态,有异常就抛出存储的异常信息,因此submit运行异常我们只能用get方法来拿到。

    至于第二点我就不多说了,平时使用中就已经证明了!

    第三点线程出异常了不是被线程池回收嘛?

    看源码我们知道线程运行最后总有一个processWorkerExit要执行,看看里面的实现

    很神奇先删掉线程又再调用创建线程的方法,所以异常线程不是被回收,而是被删除了再创建一个新的顶替了。

    到此线程池中的线程异常了会被怎么处理讲完了,总结一下就是:

    1、execute方法,可以看异常输出在控制台,而submit在控制台没有直接输出,必须调用Future.get()方法时,可以捕获到异常。

    2、一个线程出现异常不会影响线程池里面其他线程的正常执行。

    3、线程不是被回收而是线程池把这个线程移除掉,同时创建一个新的线程放到线程池中。

    4、还有源码是个好东西,答案都在里面,就是太难看懂了

    相关文章

      网友评论

          本文标题:70%人答不全!线程池中的一个线程异常了会被怎么处理?

          本文链接:https://www.haomeiwen.com/subject/bjtewhtx.html