女同事问狗哥什么是线程池的阻塞队列?
线程池
欢迎来到狗哥多线程系列连载。本篇是线程相关的第八篇,前七篇分别是:
线程池的内部结构
来源:拉勾教育 Java 并发编程.png
如图所示,线程池的内部结构主要由线程池管理器、工作线程、任务队列以及任务四部分组成。
线程池的阻塞队列
先上张图,表格左侧是线程池,右侧为它们对应的阻塞队列,你可以看到 5 种线程池对应了 3 种阻塞队列。
图源:拉勾教育 Java 并发编程~阻塞队列.png
下面逐一说下它们的特点:
- LinkedBlockingQueue,底层是链表结构、采用先进先出原则,默认容量是 Integer.MAX_VALUE,几乎可以认为是无界队列(几乎不可能达到这个数)。而由于 FixedThreadPool 和 SingleThreadExecutor 的线程数是固定的,所以只能用容量无穷大的队列来存任务。
- SynchronousQueue,容量为 0,不做存储,只做转发。由于 CachedThreadPool 的最大线程数是 Integer.MAX_VALUE,有任务提交就转发给线程或者创建新线程来执行,并不需要队列存储任务。所以在自定义使用 SynchronousQueue 的线程池应该把最大线程数设置得尽量大,避免任务数大于最大线程数时,没办法把任务放到队列中也没有足够线程来执行任务的情况。
- DelayedWorkQueue,内部用的是堆数据结构,初始容量为 16,跟 hashmap 一样动态扩容,对任务延时长短进行排序。
为什么不自动创建线程池?
阿里巴巴 Java 规约也约定了,手动创建线程池,效果会更好。为什么呢?回答这个问题之前,先来看看五种线程池初始化的方法:
// FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0 L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue <
Runnable > ());
}
// SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0 L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue <
Runnable > ()));
}
首先是 FixedThreadPool 和 SingleThreadExecutor,它两的问题在于都是用默认容量的无界队列 LinkedBlockingQueue,当任务处理慢时,队列迅速积压任务并占用大量内存,发生 OOM(内存溢出)。所以在使用时我们可以根据业务指定队列长度:
ExecutorService threadPool = new ThreadPoolExecutor(2, 5,
1 L, TimeUnit.SECONDS,
new LinkedBlockingQueue < > (3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
然后是 CachedThreadPool,也可以发现一个问题:它默认的最大线程是 Integer.MAX_VALUE,当任务贼多时,它就会不断创建线程,而线程执行比较耗时来不及回收。最终也会造成 OOM,所以应该手动指定最大线程数。
// CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60 L, TimeUnit.SECONDS, new SynchronousQueue < Runnable > ());
}
最后是 ScheduledThreadPool 和 ScheduledThreadPoolExecutor,这两问题就更大了。首先最大线程数是 Integer.MAX_VALUE,然后阻塞队列是 DelayedWorkQueue,它也是无界队列,最终还是会造成 OOM。
// ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
-END-
如果看到这里,喜欢这篇文章的话,请帮点个好看。微信搜索「一个优秀的废人」,关注后回复「 1024」送你一套完整的 java 教程(包括视频)。回复「 电子书」送你全编程领域电子书 (不只Java)。
教程节选
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- postgres数据库不能用ip地址访问的问题
- Flutter基础widgets教程-ButtonBar篇
- 如何在linux服务上创建samba文件共享服务
- Salesforce Javascript(二) 箭头函数
- java面试基础部分;
- 7-2.表单-HTML基础
- 如何在github上传多个项目
- Flutter基础widgets教程-Card篇
- Springboot热部署,使用spring-boot-devtools、springloaded两种方式进行热部署
- SpringBoot中jsp里面的静态资源js、css、images访问不到解决办法
- spring boot jsp里面的静态资源访问不到解决办法
- Flutter基础widgets教程-Center篇
- 如何上传项目到GitHub
- ES6编码规范
- Flutter基础widgets教程-Checkbox篇