2025年java高级特性多线程基础头歌

java高级特性多线程基础头歌今天看了一篇关于线程池源码的文章 写的很棒 在此推荐给大家 同时记录一下方便自己回看 线程池之 ThreadPoolEx 线程池源码分析笔记 因源码部分早已弄懂 所以我更多关注的是实际使用时的需注意事项 一 创建线程池时候要指定与业务相关的名字 以便于追溯问题 我们都知道 线程池中的线程最终是通过 ThreadFactor 产出的 那么要改线程名字

大家好,我是讯享网,很高兴认识大家。



今天看了一篇关于线程池源码的文章,写的很棒,在此推荐给大家,同时记录一下方便自己回看【线程池之ThreadPoolExecutor线程池源码分析笔记】,因源码部分早已弄懂,所以我更多关注的是实际使用时的需注意事项。

一、创建线程池时候要指定与业务相关的名字,以便于追溯问题

我们都知道,线程池中的线程最终是通过ThreadFactory产出的,那么要改线程名字,势必要去了解下ThreadFactory的源码,话不多说,下面贴出源码:

由上代码 DefaultThreadFactory 的实现可知:

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

从上知道我们只需对 DefaultThreadFactory 的代码中 的初始化做手脚,当需要创建线程池是传入与业务相关的 namePrefix 名称就可以了,代码如下(为方便直接拷贝使用,我已把个人信息注释消除):

讯享网

那么创建线程池就可以指定线程名了,测试代码如下:

 

java高级特性多线程基础头歌

. 运行结果如下,一目了然,出现异常可以准确知道是哪种线程报的错:

java 创建线程池如何正确设置线程数_内存泄漏

后续补充:

当然还有更简单的方法,我们可以直接使用Spring封装好的Executor来创建线程池,所有属性根据需要来设置即可,创建好后直接交由Spring管理,举例如下:

讯享网



二、如果在线程池中用了ThreadLocal,要警惕内存泄露问题

看下面内存泄露的例子

 
  • 代码(1)创建了一个核心线程数和最大线程数为 5 的线程池,这个保证了线程池里面随时都有 5 个线程在运行。
  • 代码(2)创建了一个 ThreadLocal 的变量,泛型参数为 LocalVariable,LocalVariable 内部是一个 Long 数组。
  • 代码(3)向线程池里面放入 50 个任务
  • 代码(4)设置当前线程的 localVariable 变量,也就是new了一个LocalVariable 放入当前线程的 threadLocals 中。

由于没有调用线程池的 shutdown 或者 shutdownNow 方法所以线程池里面的用户线程不会退出,进而 JVM 进程也不会退出。
重点是在线程池shutDown之前ThreadLocal可能产生内存泄露。

可以看到在没有对ThreadLocal进行清除时,也就是没有执行上面的方法,Jconsole的堆内存使用量如下:

java 创建线程池如何正确设置线程数_内存泄漏_02

在放开注释,执行方法清除ThreadLocal值后,Jconsole的堆内存情况如下:

java 创建线程池如何正确设置线程数_oom_03

结果一目了然,当主线程处于休眠时候,运行结果一的进程占用了大概 77M 内存运行结果二则占用了大概 25M 内存,可知运行代码一时候内存发生了泄露…

原因:
运行结果一的代码,在设置线程的 localVariable 变量后没有调用 localVariable.remove()方法,导致线程池里面的 5 个线程的 threadLocals 变量里面的 new LocalVariable() 实例没有被释放,虽然线程池里面的任务执行完毕了,但是线程池里面的 5 个线程会一直存在直到 JVM 进程被杀死。

这里需要注意的是由于 localVariable 被声明了 static,虽然线程的 ThreadLocalMap 里面是对localVariable的弱引用,localVariable也不会被回收。

运行结果二的代码由于线程在设置 localVariable 变量后及时调用了 localVariable.remove() 方法进行了清理,所以不会存在内存泄露。

总结:线程池里面设置了 ThreadLocal 变量一定要记得及时清理,因为线程池里面的核心线程是一直存在的,如果不清理,那么线程池的核心线程的 threadLocals 变量一直会持有 ThreadLocal 变量。

三、建议线程池所有参数自定义,防止OOM等问题

这个就不展开讲了,在阿里代码规范里也有说明,线程池要自定义,不要用原生的,比如CachedThreadPool:最大线程范围是Int的最大值,可能会创建大量线程导致OOM。

另外核心线程和最大线程的取舍也是有讲究的,可以根据和来设置。

还有拒绝策略也有讲究,一般如果一定要执行任务,没什么问题的话,就在拒绝策略发生时,抛给调用线程执行,如果其他情况就要换策略,这个还是具体情况具体分析的…

总结:

  1. 创建线程池需传自定义的ThreadFactory来实现线程名的定制
  2. 线程池中用完ThreadLocal要主动清除,防止内存泄露
  3. 线程池自定义,也就是所有参数需视情况来自定义。
小讯
上一篇 2024-12-27 14:24
下一篇 2024-12-30 21:31

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/5757.html