倚楼听风雨
淡看江湖路

[转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯 并发编程网 - ifeve.com

声明:本篇文章转载自并发编程网 – ifeve.com作者加多大佬的文章,目的是更好的阐释《阿里巴巴Java开发规约》第一章中的并发处理篇关于创建线程或线程池是为什么要指定有意义的线程名称相关问题。由于老四在本篇文章中优化改编了某些阐述语言,并修改了一些源码排版,再加上未找到加多的联系方式,所以如果发现本篇文章涉及到侵权问题,请及时联系老四,以便立刻删除。

本文的主要目的是解释《阿里巴巴Java开发规约第一章-并发处理篇》中第二条”创建线程或线程池时请指定有意义的线程名称,方便出错时回溯”的相关问题,一下是正文:

我们在日常开发中,当一个应用中需要创建多个线程或者线程池时候最好给每个线程或者线程池根据业务类型设置具体的名字,以便于在出现问题时候方便进行定位出错地址,下面就通过实例来说明不设置时候为何难以定位问题,以及如何进行线程或者线程池名称的设置。

1.为什么创建线程需要带上自定义的线程名称

下面通过简单的代码来说明不指定线程名称为何难定位问题,代码如下:

以上代码分别创建了两个线程,不过代码执行的过程中可能看到如下所示的异常:

[转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯的图片-高老四博客 第1张

从异常日志中我们可以看到Thred-0抛出了NPE(NullPointerException, 空调格指针异常),那么单看这个日志根本无法判断是订单模块的线程抛出的异常,首先我们分析下这个Thread-0是怎么来的,这要看下创建线程时候的代码(来源于jdk: java.lang.Thread.class):

从Thread.class源码类中,我们可以看出如果调用了没有指定线程名字的方法创建了线程,内部会使用”Thread-“+nextThreadNum()作为线程的默认名字。可知threadInitNumber是static变量,nextThreadNum是static方法,所以线程的编号是全应用唯一的并且是递增的,另外这里由于涉及到了多线程递增threadInitNumber也就是执行读取-递增-写入操作,而这个是线程是不安全的,所以又使用了方法级别的synchronized进行了同步。关于Java中static的基本使用可以参考一下老四的这篇《Java面向对象之类成员浅析》文章。

所以当一个系统中有多个业务模块并且每个模块中都有自己要跑的线程,如果遇到问题除非是抛出与业务相关的异常,否则要是都抛出类似上面的NPE异常,根本没法判断是哪一个模块出现了问题,现在将以上代码修改如下:

再次运行,异常信息如下图所示,在创建线程的时候给线程指定了一个与具体业务模块相关的名字,从运行结果就可以定位到是保存订单模块抛出了NPE异常,一下子就可以定位到问题。

[转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯的图片-高老四博客 第2张

2.为什么创建线程池时候也需要指定线程池的名称?

同理下面通过简单的代码来说明不指定线程池名称为何难定位问题,代码如下:

运行代码输出结果如下:

[转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯的图片-高老四博客 第3张

同理我们并不知道是那个模块的线程池抛出了这个异常,那么我们看下这个pool-1-thread-1是如何来的。其实是使用了线程池默认的ThreadFactory,翻看线程池创建的源码如下(源码来自jdk: java.util.concurrent.ThreadPoolExecutor.class & java.util.concurrent.Executor.class & java.util.concurrent.Executor$DefaultThreadFactory):

如上代码DefaultThreadFactory的实现可知:

  • poolNumber是static的原子变量用来记录当前线程池的编号是应用级别的,所有线程池公用一个,比如创建第一个线程池时候线程池编号为1,创建第二个线程池时候线程池的编号为2,这里pool-1-thread-1里面的pool-1中的1就是这个值。
  • threadNumber是线程池级别的,每个线程池有一个该变量用来记录该线程池中线程的编号,这里pool-1-thread-1里面的thread-1中的1就是这个值。
  • namePrefix是线程池中线程的前缀,默认固定为pool。
  • 具体创建线程,可知线程的名称使用namePrefix + threadNumber.getAndIncrement()拼接的。

从上知道我们只需对实现ThreadFactory并对DefaultThreadFactory的代码中namePrefix的初始化做手脚,当需要创建线程池是传入与业务相关的namePrefix名称就可以了,代码如下:

然后运行执行结果如下:

[转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯的图片-高老四博客 第4张

从ASYN-ACCEPT-POOL-1-thread-1就可以知道是接受链接线程池抛出的异常。

本文转载自并发编程网-ifeve.com,原文链接: 创建线程以及线程池时候要指定与业务相关的名字,以便于追溯问题

更博不易,如果觉得文章对你有帮助并且有能力的老铁烦请赞助盒烟钱,点我去赞助。或者扫描文章下面的微信/支付宝二维码打赏任意金额,老四这里抱拳了。赞助时请备注姓名或者昵称,因为您的署名会出现在赞赏列表页面,您的赞赏钱财也会被用于小站的服务器运维上面,再次抱拳。

赞(19) 给你买杜蕾斯
本站原创文章受自媒体平台原创保护,未经允许不得转载高老四博客 » [转载]创建线程或线程池时请指定有意义的线程名称,方便出错时回溯

开始你的表演 抢沙发

觉得文章有用就打赏一下老四,鼓励我更好的创作

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册