线程池参数到底要怎么配?

文章目录

  • 1 线程池快速回顾
  • 2 现有设置参数的方法及不足
  • 3 如何设置核心线程数(corePoolSize)
  • 4 如何设置最大线程数(maxPoolSize)
  • 5 如何改变等待队列长度

想必大家对Java里面线程池( 类)一定不陌生吧,无论是在日常工作还是面试题里都经常会有它的身影,特别是在当前CPU动辄就是好多核的背景下,了解并使用线程池已经成为一名合格后端开发的基本功了。

相信大家也一定思考过一个问题,面对各种各样的场景,线程池的参数到底应该怎么设计呢?这一定是一个超级难以回答的问题,几天前的我也想不到一个标准的答案,好在是发现了美团在2020年发表过的一篇文章,里面给了一个非常高级的操作——让线程池的参数动态化,这就极大地提高了系统的自适应能力。

https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html

至于为什么我现在才看到=_=,可能因为是太懒了吧。。。好在及时发现,在此基础上进行一些分析,不理解线程池的小伙伴们也不用担心,我们首先来回顾一下它的核心思想,在此技术上介绍如何将参数动态化起来~


1 线程池快速回顾

《Java 并发编程的艺术》中提到了使用线程池的好处,概括起来如下:

  • 降低资源损耗。通过重复利用已创建的线程降低线程创建和销毁的损耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。使用线程池可以进行统一的分配,调优和监控。

Java里使用线程池,主要就是用的ThreadPoolExecutor类,先来看一下 ThreadPoolExecutor 类中的构造方法:

/*** 用给定的初始参数创建一个新的ThreadPoolExecutor。*/
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量int maximumPoolSize,//线程池的最大线程数long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间TimeUnit unit,//时间单位BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

ThreadPoolExecutor 中最重要的参数:

  • corePoolSize:核心线程数。最小可以同时运行的线程数。
  • maximumPoolSize:当队列中存放的任务达到队列容量的时候,当前可以同时运行的最大线程数。
  • workQueue:当新任务来的时候会先判断当前运行的线程数量是否达到corePoolSize,如果达到的话,新任务就会被存放在队列中。如果workQueue已经满了的话就执行拒绝策略。

ThreadPoolExecutor 的其他参数:

  • keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime 才会被销毁。
  • unit : keepAliveTime 参数的时间单位。
  • threadFactoryexecutor 创建新线程的时候会用到。
  • handler:拒绝策略。

当参数设置完毕后,线程池的工作原理具体是什么呢?我们可以通过下面这个面试题来理解一下:

假设我们设置的线程池参数为:corePoolSize=10, maximumPoolSize=20,queueSize = 10
20个并发任务过来,有多少个活跃线程?

10个。corePoolSize打满,queueSize 也满

21个并发任务过来,有多少个活跃线程?

11个。corePoolSize打满,queueSize 也满还多一个,maximumPoolSize = 20,所以corePoolSize + 1此时活跃的为11个。

30个并发任务过来,有多少个活跃线程?

20个。corePoolSize打满,queueSize 也满,corePoolSize扩充至20,此时有20个活跃任务。

31个并发任务过来,有多少个活跃线程?

20个。corePoolSize打满,queueSize 也满,corePoolSize扩充至20还多一个,如果是丢弃策略,此时有20个活跃任务。


上面的流程可以总结成如下所示的流程图:(来源于https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)
在这里插入图片描述

2 现有设置参数的方法及不足

回顾完线程池的核心技术点之后就要开始思考本文主要讨论的内容了:线程池参数应该如何设置?

如果你把这个问题输入到浏览器里,极大可能是下面这种答案:
在这里插入图片描述
上面的理论看似很华丽,但现实却是很残酷的。。。你会发现虽然按照上面的指导思想进行配置了,但效果并不能让人满意,造成这种后果的原因有很多,包括但不仅限于:

  1. 任务到底是CPU还是IO密集的特征不明显
  2. 同一个机器上可能部署不止一个服务,不同服务之间也会抢占资源

针对上述问题,美团给出的对应的解决方案就是——线程池参数动态化

那么如何实现参数动态化呢?

接触过微服务开发的同学们可能就会想到了,我们完全可以借助一个配置中心来做,这样就能够实现线程池参数的动态配置和即时生效(在阿里内部也有一个专门的中间件,diamond),省去了重新部署程序并发布的步骤,通常在企业里这一系列流程下来还是比较费时间的。

在这里插入图片描述
(来源于https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)

3 如何设置核心线程数(corePoolSize)

其实 ThreadPoolExecutor 类库里直接就有这个方法:

public void setCorePoolSize(int corePoolSize) {if (corePoolSize < 0)throw new IllegalArgumentException();int delta = corePoolSize - this.corePoolSize;this.corePoolSize = corePoolSize;if (workerCountOf(ctl.get()) > corePoolSize)interruptIdleWorkers();else if (delta > 0) {// We don't really know how many new threads are "needed".// As a heuristic, prestart enough new workers (up to new// core size) to handle the current number of tasks in// queue, but stop if queue becomes empty while doing so.int k = Math.min(delta, workQueue.size());while (k-- > 0 && addWorker(null, true)) {if (workQueue.isEmpty())break;}}
}

在这里插入图片描述
我们直接看英文注释,这就是作者直接想要表达的意思。大致翻译一下:

设置线程的核心数量,如果新的corePoolSize值小于当前corePoolSize值,多出来的线程将在其下次空闲时被终止。如果新的corePoolSize值大于当前corePoolSize值,就可以创建新的worker来执行队列里的任务

在这里插入图片描述
(来源于https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)

4 如何设置最大线程数(maxPoolSize)

同样地 ThreadPoolExecutor 类库里也有这个方法:

public void setMaximumPoolSize(int maximumPoolSize) {if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)throw new IllegalArgumentException();this.maximumPoolSize = maximumPoolSize;if (workerCountOf(ctl.get()) > maximumPoolSize)interruptIdleWorkers();
}

这个方法的注释和上面的方法类似,大家可以对照着看:
在这里插入图片描述
逻辑也并不复杂:

  1. 参数校验
  2. 设置最大线程数 maxPoolSize
  3. 如果工作线程数是否大于最大线程数,则对空闲线程发起中断

JDK原生线程池ThreadPoolExecutor还提供了其他设置参数的方法:
在这里插入图片描述

5 如何改变等待队列长度

等待队列的长度capacityfinal修饰符修饰,所以按理说是不能修改的

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

唯一可能的办法就是自己定义一个队列,在美团的实现里就是一个名为ResizableCapacityLinkedBlockIngQueue的队列,根据名称也不难看出,这个队列的容量是可变的。

具体的实现细节美团好像并没有公布出来,不过我们可以简单的将原先LinkedBlockingQueuecapacityfinal修饰符去掉,并提供getter和setter方法,形成我们自己的ResizableCapacityLinkedBlockIngQueue

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555473.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle嵌套三层循环语句,在存储过程中执行3种oracle循环语句

http://www.cnblogs.com/coprince/p/3443219.htmlcreate or replace procedure pr_zhaozhenlong_loop/*名称&#xff1a;在存储过程中执行3种循环语句功能&#xff1a;利用循环给表中插入数据调用&#xff1a;begin-- Call the procedurepr_zhaozhenlong_strsql;end;创建人&…

彻底搞懂Cookie、Session、JWT和Token

文章目录引入&#xff1a;http是一个无状态协议&#xff1f;怎么解决呢&#xff1f;一、Cookie和Session1.1 cookie 注意事项&#xff1a;1.2 cookie 重要的属性1.3 session 注意事项&#xff1a;1.4 Cookie 和 Session 的区别&#xff1a;二、token&#xff08;令牌&#xff0…

oracle数据库导入txt,oracle数据库导入TXT文件方法介绍

客户端连接数据库导入1. 安装有oracle客户端&#xff0c;配好监听。2. 以oracle数据库app用户的表user_svc_info为例CREATE TABLE USER_SVC_INFO(PHONE varchar2(20) NOT NULL,SVC_ID varchar2(32) NOT NULL,P_USERNAME varchar2(100) NULL,USER_STATUS number NOT NULL ,P_ALI…

你真的知道什么是多线程吗?为什么要学习多线程?

文章目录1、多线程的含义2、原理3、优势4、线程与进程的区别5、线程与多线程的区别6、线程调度的分类7、同步与异步8、并发与并行9、为什么要使用线程池10、线程池的好处11、线程池的分类12、意义1、多线程的含义 多线程&#xff08;multithreading&#xff09;&#xff0c;是…

oracle 表关联索引优化,Oracle执行计划调优-超级大表关联超级小表的性能调优

今日客户现场出现一个查询SQL异常慢的情况。用时分钟级别。SELECT *FROM (SELECT a1.*, rownum rnFROM (SELECT openOrder2017.exchId,............openOrder2017.internalbizmark,customer.typeIdListFROM openOrder2017, customerWHERE openOrder2017.custId customer.custI…

Common Sort - 排序 - Java

文章目录排序概念稳定性&#xff08;重要&#xff09;应用 - 举例1.、各大商城的价格从低到高等2、中国大学排名常见的排序算法&#xff08;8 种&#xff09;- 总览直接插入排序模拟实现 - 插入排序稳定性分析结论希尔排序思考原理科学家的分组思维模拟实现 - 希尔排序总结选择…

linux的运行级别如何更改成6,把Linux运行级别设置为6后如何解决的经验分享

我们知道&#xff0c;Linux有7个运行级别&#xff0c;而运行级别设置为6后&#xff0c;会导致Linux系统刚启动完成就立刻重启&#xff0c;重启后又会立刻重启&#xff0c;如此反复&#xff0c;导致系统不能正常运行。本文笔者和大家分享一下误把Linux运行级别设置为6后如何解决…

flume linux 命令,Linux环境Flume安装配置及使用

# Flume监听本地Linux-hive日志文件采集到HDFS——配置文件# Name the components on this agent agent别名设置a1.sources r1a1.sinks k1a1.channels c1# Describe/configure the source 设置数据源监听本地文件配置# exec 执行一个命令的方式去查看文件 tail -F 实时查看a…

Redis五种数据结构应用场景

文章目录前言二、字符串String2.1、常用操作2.2、应用场景2.2.1、单值缓存&#xff08;最常用&#xff09;2.2.2、对象缓存2.2.3、分布式锁2.2.4、计数器三、哈希hash3.1、常用操作3.2、应用场景3.2.1、对象缓存3.2.2、 电商购物车四、列表list4.1、常用操作4.2、应用场景4.2.1…

linux能记录日志的终端,Linux上的日志系统

Linux上的日志系统1、syslog2、syslog-ng 下一代升级版日志系统红帽5使用syslog 6使用syslog-ngsyslog 服务syslogd : 系统&#xff0c;非内核产生的信息klogd : 内核&#xff0c;专门负责记录内核的日志信息系统启动时所输出的信息【到init启动之前的所有信息】&#xff1a;…

IntelliJ IDEA中的神仙插件

文章目录1. Alibaba Java Coding Guidelines2.GsonFormat3.A8Translation4.Maven Helper5.Free Mybatis plugin6.Grep Console7.Lombok8.Nyan progress bar9.FindBugs-IDEA10.Key Promoter X11.JavaDoc12.ignore13.RainbowBrackets14.Activate-power-mode15.CodeGlance16.Gener…

linux 远程拒绝服务,Linux Kernel SCTP远程拒绝服务漏洞

发布日期&#xff1a;2011-08-30更新日期&#xff1a;2011-08-30受影响系统&#xff1a;Linux kernel 2.6.x描述&#xff1a;--------------------------------------------------------------------------------BUGTRAQ ID: 49373CVE ID: CVE-2011-2482Linux Kernel是Linux操…

linux 常用命令行 大全

Linux常用命令大全&#xff08;非常全&#xff01;&#xff01;&#xff01;&#xff09; 最近都在和Linux打交道&#xff0c;感觉还不错。我觉得Linux相比windows比较麻烦的就是很多东西都要用命令来控制&#xff0c;当然&#xff0c;这也是很多人喜欢linux的原因&#xff0c…

linux运行程序+后注销,linux – 如果我启动后台进程然后注销,它会继续运行吗?...

在与同事长时间讨论之后问这个问题,我真的想在这里澄清一下.我通过添加“&”来启动后台进程到命令行或用CTRL-Z停止它并在后台用“bg”恢复它.然后我退出了.怎么了&#xff1f;我们很确定它应该被一个SIGHUP杀死,但这并没有发生;再次登录时,进程很愉快,并且pstree显示它被i…

SpringBoot使用Websocket

webSocket是HTML5的一种新协议&#xff0c;它实现了服务端与客户端的全双工通信&#xff0c;建立在传输层&#xff0c;tcp协议之上&#xff0c;即浏览器与服务端需要先建立tcp协议&#xff0c;再发送webSocket连接建立请求。webSocket的连接&#xff1a;客户端发送请求信息&…

linux误删文件咋恢复,恢复Linux误删文件

恢复Linux误删文件目录方法一方法二不小心执行了rm删除了某些文件&#xff0c;是有点倒霉。不过我一般都会设置alias rmrm -i在删除的时候需要输入Y才能真正删除&#xff0c;当然此时用rm -f那也会直接删除了。因此找了两种方案恢复误删的文件。方法一适用系统内置debugfs&…

Springboot整合Websocket遇到的坑_websocket session不支持序列化,无法存储至redis_Websocket相关问题总结(Session共享,用户多端登录等)

Springboot整合Websocket遇到的坑 一、使用Springboot内嵌的tomcat启动websocket 1.添加ServerEndpointExporter配置bean Configuration public class WebSocketConfig {/*** 服务器节点** 如果使用独立的servlet容器&#xff0c;而不是直接使用springboot的内置容器&#x…

linux 中文意思,linux 中 ~/. 是什么意思

~代表你的/home/用户明目录假设你的用户名是x&#xff0c;那么~/就是/home/x/.是代表此目录本身&#xff0c;但是一般可以不写所以cd ~/. 和cd ~ 和cd ~/效果是一样的但是.后面有东西又是另外一个问题&#xff0c;点在文件名头部&#xff0c;代表一个隐藏文件~/.local是你的主目…

图文详解mina框架

Apache Mina Server 是一个网络通信应用框架&#xff0c;也就是说&#xff0c;它主要是对基于TCP/IP、UDP/IP协议栈的通信框架&#xff08;当然&#xff0c;也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等&#xff09;&#xff0c;Mina 可以帮助我们快速开发高性能、高…

linux my.cnf基本参数,Linux中MySQL配置文件my.cnf参数说明

MySQL参数优化这东西不好好研究还是比较难懂的&#xff0c;其实不光是MySQL&#xff0c;大部分程序的参数优化&#xff0c;是很复杂的。MySQL的参数优化也不例外&#xff0c;对于不同的需求&#xff0c;还有硬件的配置&#xff0c;优化不可能又最优选择&#xff0c;只能慢慢的进…