前言
日常开发中,为了更好管理线程资源,减少创建线程和销毁线程的资源损耗,我们会使用线程池来执行一些异步任务。但是线程池使用不当,就可能会引发生产事故。今天田螺哥跟大家聊聊线程池的10个坑。大家看完肯定会有帮助的~
-
线程池默认使用无界队列,任务过多导致OOM
-
线程创建过多,导致OOM
-
共享线程池,次要逻辑拖垮主要逻辑
-
线程池拒绝策略的坑
-
Spring内部线程池的坑
-
使用线程池时,没有自定义命名
-
线程池参数设置不合理
-
线程池异常处理的坑
-
使用完线程池忘记关闭
-
ThreadLocal与线程池搭配,线程复用,导致信息错乱。
1.线程池默认使用无界队列,任务过多导致OOM
JDK开发者提供了线程池的实现类,我们基于Executors
组件,就可以快速创建一个线程池。日常工作中,一些小伙伴为了开发效率,反手就用Executors
新建个线程池。写出类似以下的代码:
/**
* 公众号:捡田螺的小男孩
*/
public class NewFixedTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(() -> {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
});
}
}
}
使用newFixedThreadPool
创建的线程池,是会有坑的,它默认是无界的阻塞队列,如果任务过多,会导致OOM
问题。运行一下以上代码,出现了OOM
。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.example.dto.NewFixedTest.main(NewFixedTest.java:14)
这是因为newFixedThreadPool
使用了无界的阻塞队列的LinkedBlockingQueue
,如果线程获取一个任务后,任务的执行时间比较长(比如,上面demo代码设置了10
秒),会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终出现OOM
。
看下newFixedThreadPool
的相关源码,是可以看到一个无界的阻塞队列的,如下:
//阻塞队列是LinkedBlockingQueue,并且是使用的是无参构造函数
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//无参构造函数,默认最大容量是Integer.MAX_VALUE,相当于无界的阻塞队列的了
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
因此,工作中,建议大家自定义线程池,并使用指定长度的阻塞队列。
2. 线程池创建线程过多,导致OOM
有些小伙伴说,既然Executors
组件创建出的线程池newFixedThreadPool
,使用的是无界队列,可能会导致OOM
。那么,Executors
组件还可以创建别的线程池,如newCachedThreadPool
,我们用它也不行嘛?
我们可以看下newCachedThreadPool
的构造函数:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
它的最大线程数是Integer.MAX_VALUE
。大家应该意识到使用它,可能会引发什么问题了吧。没错,如果创建了大量的线程也有可能引发OOM
!
笔者在以前公司,遇到这么一个OOM问题:一个第三方提供的包,是直接使用
new Thread
实现多线程的。在某个夜深人静的夜晚,我们的监控系统报警了。。。这个相关的业务请求瞬间特别多,监控系统告警OOM了。
所以我们使用线程池的时候,还要当心线程创建过多,导致OOM
问题。大家尽量不要使用newCachedThreadPool
,并且如果自定义线程池时,要注意一下