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

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

在最近的一次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的博客-CSDN博客_一个进程可以有多少个线程

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

参考:

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

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

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