方法中创建线程池,方法结束后线程池会被垃圾回收吗?
一般的线程池写法是 放在成员变量级别的, 不会是局部变量
在最近的一次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博客_一个进程可以有多少个线程
============================
参考: