方法中创建线程池,方法结束后线程池会被垃圾回收吗?

本文探讨了线程池作为局部变量时存在的问题,即方法结束并不会自动关闭线程池,可能导致资源泄漏。线程池的核心线程在无任务时会保持等待状态,不会被GC回收,因为存在引用链。解决方案包括设置为成员变量、调用shutdown方法、允许核心线程超时关闭等。同时,文章提到了验证关闭效果的方法和线程池对事务注解的影响,以及进程能创建线程的数量限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般的线程池写法是 放在成员变量级别的,  不会是局部变量

在最近的一次job改造中发现一个问题,  前人在方法里创建了线程池, 然后执行业务逻辑

    public static void main(String[] args) throws Exception {
        new xxx().runJob();
        System.out.println("end..." + 1);

        new xxx().runJob();
        System.out.println("end..." + 2);
    }

public void  runJob(){

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 10L,TimeUnit.SECONDS, new LinkedBlockingQueue<>(2000));

        // 是否允许核心线程超时 关闭
        // threadPoolExecutor.allowCoreThreadTimeOut(true);

        for (int taskNum = 0; taskNum < 5; taskNum++) {
            // 提交5个任务 到线程池
            threadPoolExecutor.execute(() -> {

                // 任务的内容是, 5秒内, 每秒打印一次序号
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                        log.info("任务被执行, 当前i:" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
}

由于该job, 6小时一次, 频率不高所以一直未被发现

因为在本地测试这块的业务, 

使用@Test 来运行测试方法, 会自动关闭线程池

使用 main 方法来跑, 发现线程一直没结束

所以突然想起这个问题, 线程池到底会不会随着方法的结束, 而自动销毁

以及上面的写法有没有问题

===========================

排查工具: idea 快照,  jvisualvm

1.自定义线程工厂, 给线程设置前缀

2.多次调用方法 runJob()

在jvisualvm 工具中就能看到不断创建的 核心线程

=============================

问题的关键点, 线程池什么情况下才会关闭?

参考源码注释

A pool that is no longer referenced in a program and has no remaining threads will be shutdown automatically.

程序中不再引用且没有剩余线程的池将被自动关闭。

所以在不做特殊设置的前提下, 5个核心线程, 会一直存活, 就算没有任务, 因为使用的阻塞队列

所以核心线程会处于 WAITING 状态

=======================

线程池不会被GC回收的原因是

存在引用关系: ThreadPoolExecutor->Worker->thread

=============================

gc回收的前提是 GC Root 不可达

1.存活的线程, 可以作为GC Root, 是无法被回收的
2. GC Root对象 引用的对象, 无法被回收

===========

如何解决这个问题

1.线程池 设置成 成员变量 (推荐)

2.使用threadPoolExecutor.shutdown(); 方法关闭线程池(会等待任务执行结束)

3.设置核心线程超时关闭 allowCoreThreadTimeOut(true);

4.核心线程数设置为0, 但在使用上会带来别的麻烦(略)

==========

如何验证解决方法是否正确的?

1.观察main()方法能够 自动关闭

2.基于 WeakReference, 打印线程池状态

3.基于ide/jvisualvm工具查看

==========
==========
==========

线程池会是否会让 事务注解失效?

==========

1个进程最多能创建多少个线程?

一个进程最多可以创建多少个线程?_小林coding的博客-优快云博客_一个进程可以有多少个线程

============================

参考:

当线程池作为局部变量时,方法结束后线程池会被垃圾回收吗?_雪落南城的博客-优快云博客_线程池定义在局部变量中

gc root总结 - 高压锅里的大萝卜 - 博客园

GC 两种算法(引用计数法和可达性分析算法) - 简书

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值