线程执行一半断了_有的线程它死了,于是它变成一道面试题

636cb90a466928ea9b2df17c8f9356a6.png

----本文首发于公众号,关注文末公众号阅读体验更佳

有些线程它活着,但它躺在池中碌碌无为;

有的线程它死了,于是它变成一道面试题。

这次的文章,要从一次阿里的面试说起。

我记得那天是周一,刚刚经历过周末过的放松,干劲十足的我正在键盘上疯狂的输出。这时,我的手机响了起来,拿起一看,是来自杭州的电话,心想这次是要给我推荐股票呢还是要让我贷款呢。我接起了电话,准备“调戏一番”。那边响起一个声音:"你好,请问是xxx吗?这边是杭州阿里巴巴,现在有时间进行电话面试吗?"。说实在的,听完这句话后,我感觉我已经身在杭州,干劲十足的在杭州的阿里的工位上"修福报"。但是我现在正在疯狂输出,没有时间,于是我说:"不好意思,现在没有时间,可以约在今天晚上8点钟吗?".

晚上如约接到了电话。我们直奔主题,在你来我往中进行了友好的技术交流。具体的面试过程就不详述了,后面有机会整理一份面试分享。整个面试过程中,有这么一道题给我留下了深刻的印象:

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

需要说明一下,文中讨论的线程池都是Executors线程池。

对于Executors线程池我可以说是烂熟于心,因为工作中用的比较的多,阅读过其源码。也是我作为面试官时必问的几个范围之一,比如以下问题:

了解JDK Executors线程池吗?知道JDK提供了哪些默认的实现吗?看过阿里巴巴java开发手册吗?知道为啥不允许使用默认的实现吗?你们没有用默认的吧?那来介绍一下你们自定义线程池的几个常用参数呗?你这个几个参数的值是怎么得来的呀?算出来的?怎么算出来的?线程池里面的任务是IO密集型的还是计算密集型的呢?好,现在我们有一个自定义线程池了,来说一下你这个线程池的工作流程呗?那你这个线程池满了怎么办呀?拒绝?咋拒绝?有哪些拒绝策略呢?别紧张,随便说两个就行。......回到开始说的阿里巴巴java开发手册不允许使用默认实现,你回答说可能会引起OOM,那我们聊聊JVM吧......

241b5b1b67fb391242d50dbb9e97cc15.png
阿里巴巴java开发手册关于线程池创建的建议

这一系列关于线程池的连环炮,就是我作为面试官时必问的几个问题。别问为什么,因为我们的招聘JD上明确写了:熟悉多线程编程。而这些问题,我觉得是熟悉多线程编程的基础。这里我也不解答了,这种文章网上还是挺多的,可以去了解一下。

这块真的很重要,我也多次给我的小伙伴强调:

69982c7ee4882679fea5d6f9de111860.png

来吧,一起分析一波

好了现在回到阿里的面试官问我的这道面试题:

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

先说说我当时的回答,因为心里没底,我的回答很犹豫也很烂!如下:

5f738dabf2311178301059920f57e48e.png

我的回答总结起来三句话:

1.抛出堆栈异常 ---这句话对了一半!
2.不影响其他线程任务---这句话全对!
3.这个线程会被放回线程池---这句话全错!

测试用例写起来

44313669479c72ef005106491fcd9a73.png

抛出堆栈异常为啥对了一半?

先让程序跑起来,我们用事实说话:

eab5ce6e03b1f34c7e4115e0ce32ae37.png

从执行结果我们看出

当执行方式是execute时,可以看到堆栈异常的输出。当执行方式是submit时,堆栈异常没有输出。

那么我们怎么拿到submit执行方式的堆栈异常呢,看图说话:

1ed46fc8ee39ce471b1c33d148caa078.png

所以,现在知道为什么回答:抛出堆栈异常只对了一半吧。

execute方法执行时,会抛出(打印)堆栈异常。
submit方法执行时,返回结果封装在future中,如果调用future.get()方法则必须进行异常捕获,从而可以抛出(打印)堆栈异常。

你以为这一部分写到这里就完事了?那不行啊,你心里没有一个疑问吗?为啥execute直接抛出异常,submit没有直接抛出异常呢?

源码之下无秘密:

当执行方式是executes时:

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

8665a5b1300b8228b23628147eb640eb.png

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

697ddecdbe72baee8fbbd75802e52d22.png

这个uncaughtException是何许人也,看java doc上咋说的:

这个方法是JVM调用的,我们只需要指定我们想要的处理方式即可。

那我们怎么指定呢:

//直接new Thread()的时候
Thread t=newThread();
t.setUncaughtExceptionHandler(newThread.UncaughtExceptionHandler()
{publicvoiduncaughtException(Thread t, Throwable e){
//根据业务场景,做你想做的 }
});
//线程池的时候:
ExecutorService threadPool = Executors.newFixedThreadPool(1, r -> {
Thread t =newThread(r);
t.setUncaughtExceptionHandler((t1, e) ->
System.out.println("根据业务场景,做你想做的:"+ e.getMessage()));returnt;}
);

当执行方式是submit时:

f5d7fd3a719b49615f8d44dad9166c53.png

其本质也是调用了execute方法,所以它还是回到java.util.concurrent.ThreadPoolExecutor#runWorker方法:

404e863ea43214da03e0fe0f181bc738.png

向前,继续跟进去看看:

2bac1ca6e405e14721abe5eb75c04222.png

java.util.concurrent.FutureTask#setException干啥了啊,瞅一眼

906021c647f8b812aa27786ef234682c.png

深呼吸,整理好思路,我们马上走向最终的真相:

09926aee3a998d9208f97cb8a7cd8842.png

好了,第一个议题【抛出堆栈异常为啥对了一半?】讨论完毕。在源码里面走了一趟,现在我们可以给出这一部分的满分答案了。


不影响其他线程任务,回答正确

这一部分我们直接上代码,运行起来看结果吧:

36b74304c3c75be98a66506a37ced100.png

代码和运行结果是不会骗人的:

线程池中一个线程异常了后,不影响其他线程任务

大家注意线程名称这个细节:1,2,3,4,6。魔鬼都在细节里啊,这个点我下面会讲,先在这里把问题抛出来:我就纳闷了,怎么没有5啊?!


这个线程会被放回线程池为啥全错了?

我们去源码里面寻找答案:

1058ca819e432b0a5efd481110038ae3.png

让源码给出答案:

5dd465cd040e52b105df8965f2870140.png

5号线程去哪里了?

f8a8623ae84d2d616aeb8aaaeb1a9abc.png

new Worker()方法会告诉你:5去哪里了。

5eaa2657d13ff6950ba80a29dc49793c.png

再配上这张由我这个灵魂画师亲自操刀画的图,一起食用,味道更佳:

f34a4702f74f2da0ddc07a9d1f431b81.png

现在知道为啥:我回答这个线程会被放回线程池为啥全错了吧。还附带送你一个线程名称变化的细节,不客气。


总结一下

当一个线程池里面的线程异常后:
当执行方式是execute时,可以看到堆栈异常的输出。
当执行方式是submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常。
不会影响线程池里面其他线程的正常执行。
线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。
不要背答案,要理解,要深入,上面说完后记得在问问面试官,需要我从源码的角度讲一讲吗?这逼装的,礼貌而不失风度。

以上,我关于《一个线程池中的线程异常了,那么线程池会怎么处理这个线程?》这个问题的见解就表达完毕,仅代表个人观点,欢迎有不同意见的小伙伴,一起讨论,一起进步。


最后说一点

这篇文章是我上周五推完上一篇文章之后就在构思并且着手准备了。大部分内容都是思考于晚上睡觉前的半小时,写于周末和工作日的早上早起的一小时。

其实想到写什么内容并不难,难的是你对内容的把控。关于技术性的语言,我是反复推敲,查阅大量文章来进行证伪,总之慎言慎言再慎言,毕竟做技术,我认为是一件非常严谨的事情,我常常想象自己就是在故宫修文物的工匠,在工匠精神的认知上,目前我可能和他们还差的有点远,但是我时常以工匠精神要求自己。就像我在群里表达的:对于技术文章(因为我偶尔也会荒腔走板的聊一聊生活,写一写书评,影评),我尽量保证周推,全力保证质量。

be63c375e965dafe218443e0988fa806.png

最后,再感叹一次:

有些线程它活着,但它躺在池中碌碌无为;

有些线程也活着,但它一刻不停忙到飞起;

有的线程它死了,被抛弃,被回收,

但是它无怨无悔,

因为它是死在执行任务的路上,

它凭借自己最后的一声呐喊

“为了新兄弟,移除我吧!”

最后,变成一道面试题。

我还没答上来。

c8455484bd730c7988c584c1e06aa7a3.png
公众号-why技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#中的扩展方法,Linq,IO和多线程的定义和实例

前段时间学C#的上转型,泛型,lambda表达式这些应用的理解很费劲。学过之后我多多的练习了几天,接下来继续复习C#的其他一些概念,说实在的这些知识点学过之后很容易忘,但是都是很重要的,所以发表在博客上没事…

准确率 召回率_吴恩达深度学习笔记(61)-训练调参中的准确率和召回率

单一数字评估指标(Single number evaluation metric)无论你是调整超参数,或者是尝试不同的学习算法,或者在搭建机器学习系统时尝试不同手段,你会发现,如果你有一个单实数评估指标,你的进展会快得多,它可以快…

CDU集训代码:基础算法和数据结构2

做题地址&#xff1a; http://acm.hdu.edu.cn/diy/contest_login.php?cid16636 Problem A: HDU1040 排序题&#xff0c;可以直接使用c提供的排序&#xff0c;如果是JAVA&#xff0c;也可以使用自带的排序方法。C语言需要自己写排序算法。 #include<cstdio> #include<…

[html] 如何禁止input输入的历史记录

[html] 如何禁止input输入的历史记录 给form加上 autocompleteoff 可以禁止整个表单的历史记录给单个input加上 autocompleteoff 可禁止这个input的历史记录个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起…

python迷宫最短路径_python实现最短路径的实例方法

解决最短路径问题&#xff1a;(如下三种算法)(1)迪杰斯特拉算法(Dijkstra算法)(2)弗洛伊德算法(Floyd算法)(3)SPFA算法第一种算法&#xff1a;Dijkstra算法广度优先搜索解决赋权有向图或者无向图的单源最短路径问题.是一种贪心的策略算法的思路声明一个数组dis来保存源点到各个…

[html] html如何创建图片热区(img usemap)?

[html] html如何创建图片热区&#xff08;img usemap&#xff09;&#xff1f; <img src"china.gif" usemap"#mymap"><map name"mymap"><area shape"rect" href"a.html" coords"0,0,50,50"><…

探讨LoadRunner的并发用户和集合点

探讨LoadRunner的并发用户和集合点 近来跟踪一个项目&#xff0c;发现同事们在执行性能测试时&#xff0c;比较热衷于使用集合点&#xff0c;从概念上认为要得到并发用户就必须设置集合点&#xff0c;认为在执行一个压力测试脚本时&#xff0c;设置了集合点才算是有效的并发用…

python在职场的用处大吗_Python未来发展怎么样,未来办公是否都需要精通Python?...

首先&#xff0c;要先了解Python是什么&#xff1f;Python是一种计算机程序设计语言&#xff0c;又被称为胶水语言&#xff0c;可以用混合编译的方式使用c/c/java等语言的库。你可能已经听说过很多种流行的编程语言&#xff0c;比如在大学里感觉非常难学的C语言&#xff0c;进入…

spring gateway 鉴权_通过spring实现service变成controller,代码得到了简化

在网上发现了一个牛X的思路&#xff0c;在做restful的时候&#xff0c;如果业务改变&#xff0c;需要每次都修改controller&#xff0c;后来方便了&#xff0c;直接透传的方式&#xff0c;其实也比较麻烦&#xff0c;每次都要写controller。需求变了接口也发生了改变&#xff0…

java中return返回值_Java中return的用法

展开全部一、return语句总是用在方法中&#xff0c;有两个作用。一个是返回方法指定类型的值(这个值总62616964757a686964616fe59b9ee7ad9431333366306434是确定的)。一个是结束方法的执行(仅仅一个return语句)。二、实例1 。返回一个String。private String gets(){String s …

Magento 模块详解

模块&#xff08;module&#xff09;是Magento的核心。站点上的任何 一个动作&#xff08;action&#xff09;&#xff0c;无论是在前台和还是在后台的每一个操作都是通过模块来实现的。模块是可以视为一个容器&#xff0c;它可包 含下面这几项&#xff1a;设置(settings)&…

python运行别人的项目_pycharm实现在虚拟环境中引入别人的项目

如果你想引入别人的项目&#xff0c;但是呢引入的项目可能与自己原先装的模块的版本产生冲突&#xff0c;而且如果引入一个项目就在本地进行运行使用&#xff0c;每个项目用的依赖包都不大相同&#xff0c;就会导致解释器安装包过多&#xff0c;就会导致加载过慢&#xff0c;甚…

java 事件分发线程_深入浅出Java多线程(2)-Swing中的EDT(事件分发线程) [转载]...

本系列文章导航本文主要解决的问题是&#xff1a;如何使其Swing程序只能运行一个实例&#xff1f;抛开Swing&#xff0c; 我们的程序是通过java 命令行启动一个进程来执行的&#xff0c;该问题也就是说要保证这个进程的唯一性&#xff0c;当然如果能够访问系统的接口&#xff0…

java redis 分布式锁_使用Redis单实例实现分布式锁

一、前言在同一个jvm进程中时&#xff0c;可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题&#xff0c;但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源时候&#xff0c;juc包的锁就无能无力了&#xff0c;这时候就需要分布式锁了。常见…

设计一个按优先数调度算法实现处理器调度的程序_计算机中的程序都是怎么运行的,来深入了解一下吧...

在现代计算机操作系统中&#xff0c;总是会保持多道程序环境。一个作业被提交后&#xff0c;通常经过作业调度和进程调度后&#xff0c;才能获得处理机。有时为提高内存利用率&#xff0c;还会设置中程调度。那我们先来了解一下处理机调度的层次吧。高级调度&#xff0c;又称作…

从数据库加载程序集

有的做MSCRM2011开发的朋友会奇怪&#xff0c;注册的Plugin可以注册在数据库中&#xff0c;那么它如何去加载这个dll呢。最近正好有空&#xff0c;简单地实现了一下从数据库加载程序集&#xff0c;关键的地方是需要使用Assembly.Load方法的一个重载&#xff1a; Assembly.Load(…