网站首页 > 技术文章 正文
前言
线程池的核心参数配置在网上有一大堆的文章介绍,这次结合个人理解写一篇文章记录一下,以便加深印象和后续查阅。
复制代码
线程池配置参数
corePoolSize:线程池核心线程数 maximumPoolSize:线程池最大线程数 keepAliveTime:允许线程空闲时间(对非核心工作线程的回收) TimeUnit:线程空闲时间单位 workQueue:线程队列(当核心线程数满了,新的任务就会放入这个队列中) threadFactory:线程工厂(用于创建工作线程,自定义线程工厂可以指定线程名称) handler:线程池拒绝策略(当线程队列满了且最大线程数也满了,就会执行任务拒绝策略,默认有4种) allowCoreThreadTimeOut:控制核心工作线程是否需要被回收
常规线程池参数配置
- 首先提问一个面试题:现有1000个任务,10台服务器,每台机器都是4核,在任务不丢弃情况下,线程池参数该怎么配置最合理呢?
- 把这个问题拆分一下,1000个任务,10台机器,那么每台机器就负责100个任务(常规轮训负载均衡模式,不考虑其他额外情况),每台机器都是4核,那么就可以设置核心线程数和最大线程数为4,线程队列大小为96即可。
- 当然也可以把核心和最大线程数设置为5(n+1)个,线程队列大小为95,这样是为了防止线程偶尔由于页缺失故障或者其他原因暂停,出多来的一个线程也能确保CPU的调度时钟周期不会被浪费,相当于备用线程。
复制代码
- 如果任务是CPU密集型配置:工作线程 = cpu核心数 + 1;
- 如果任务是IO密集型场景:工作线程 = cpu核心数 * 2;
- 所以上面例子中就是基于CPU密集型任务配置线程池。而且网上大部分文章描述线程池配置也是基于这两点来分析的。
- 可惜理想很丰满,现实很骨感。在实际工作场景中,其实没那么容易区分线程中执行的任务是CPU密集还是IO密集,而且服务器上还会有其他应用线程抢占CPU资源,就算还有一些其他的公式计算配置线程池参数,那也是基于理想场景情况下进行配置的,所以上述配置更多的还是应用于面试中。
动态配置线程池参数
- 上述中既然不能一次定义适配所有场景的线程池参数,那么如果可以根据不同业务场景动态配置线程池参数,通过人工干预介入来适配大部分场景也行的
- 正好在JDK的自定义线程池ThreadPoolExecutor里,提供了动态扩展线程池核心参数的方法
- 可以在运行期间的线程池使用此方法可以覆盖原来配置的值:
ThreadPoolExecutor线程池提供了5种配置参数可供动态更新:核心线程池,最大线程数,线程工厂,线程空闲时间,拒绝策略。
复制代码
- 这里主要讨论的是核心线程池和最大线程池两种参数配置:
/**
*
* @Author: ZRH
* @Date: 2021/10/08 17:30
*/
@Slf4j
public class ExecutorTest {
public static void main (String[] args) throws Exception {
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
3,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(7),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> {
try {
logExecutorInfo(threadPoolExecutor);
Thread.sleep(2000);
} catch (InterruptedException e) {
}
});
}
logExecutorInfo(threadPoolExecutor);
threadPoolExecutor.setCorePoolSize(5);
threadPoolExecutor.setMaximumPoolSize(5);
logExecutorInfo(threadPoolExecutor);
Thread.currentThread().join();
}
private static void logExecutorInfo (ThreadPoolExecutor executor) {
log.info("线程池核心线程数=" + executor.getCorePoolSize() +
", 线程池最大线程数=" + executor.getMaximumPoolSize() +
", 线程池队列剩余任务=" + executor.getQueue().size() +
", 线程池活跃线程数=" + executor.getActiveCount() +
", 线程池任务完成数" + executor.getCompletedTaskCount());
}
}
复制代码
- 看执行结果:刚开始线程池里核心线程数2个、最大线程数3个、剩下7个队列。活跃的线程也只有3个。
- 然后更改核心线程和最大线程数为5后,线程池里对应的核心线程数和最大线程数也增加至5个,活跃的工作线程也是5个。说明更改配置成功。
- 注:更新线程池参数时,核心线程数不能超过最大线程数配置。否则配置最后不会生效。
public static void main (String[] args) throws Exception {
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
3,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(7),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> {
try {
logExecutorInfo(threadPoolExecutor);
Thread.sleep(2000);
} catch (InterruptedException e) {
}
});
}
logExecutorInfo(threadPoolExecutor);
threadPoolExecutor.setCorePoolSize(5);
// threadPoolExecutor.setMaximumPoolSize(5);
logExecutorInfo(threadPoolExecutor);
Thread.currentThread().join();
}
复制代码
- 上图中把核心线程数更新为5,最大线程数不改动任为3。最后看执行结果,最终的活跃线程还是3个,说明配置没有生效,具体源码在ThreadPoolExecutor类的getTask()方法里,感兴趣的同学可以去看一下...
动态更新线程队列
- ThreadPoolExecutor线程池并没有动态配置线程池队列大小的方法
- 想自己操作一下也是很简单的,只需要自定义实现一个队列,可以直接把LinkedBlockingQueue复制一份,并把capacity参数设定为可更改
public static void main (String[] args) throws Exception {
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
3,
30,
TimeUnit.SECONDS,
new CustomLinkedBlockingQueue<>(7),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> {
try {
logExecutorInfo(threadPoolExecutor);
Thread.sleep(2000);
} catch (InterruptedException e) {
}
});
}
logExecutorInfo(threadPoolExecutor);
threadPoolExecutor.setCorePoolSize(5);
threadPoolExecutor.setMaximumPoolSize(5);
CustomLinkedBlockingQueue queue = (CustomLinkedBlockingQueue)threadPoolExecutor.getQueue();
queue.setCapacity(10);
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(() -> {
try {
logExecutorInfo(threadPoolExecutor);
Thread.sleep(2000);
} catch (InterruptedException e) {
}
});
}
Thread.currentThread().join();
}
复制代码
- 看结果,后续添加的任务会放入队列中,并且队列大小也超过第一次设置大小,说明配置成功
作者:超级爽朗的郑
链接:https://juejin.cn/post/7016639323369898015
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
猜你喜欢
- 2024-10-22 java线程池参数及使用 java线程池的用法
- 2024-10-22 面试官:说说你对线程池的了解 线程池实现原理面试
- 2024-10-22 「每日分享」高阶程序员需要掌握的常见性能优化策略
- 2024-10-22 池化技术学习 池化方法
- 2024-10-22 「Java基础」「多线程」-线程池 java多个线程池
- 2024-10-22 线程池配置的常见误区 线程池配置参数有哪些
- 2024-10-22 创建线程池参数有哪些作用? 创建线程池的7个参数
- 2024-10-22 一文搞懂!多线程之间的通信及线程池
- 2024-10-22 码农大叔带你——解析线程池 线程池的主要处理流程
- 2024-10-22 Java中线程池,你真的会用吗? java 线程池常用方法
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)