Java性能调优实战
刘超
金山软件西山居技术经理
立即订阅
7535 人已学习
课程目录
已完结 48 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 怎样才能做好性能调优?
免费
模块一 · 概述 (2讲)
01 | 如何制定性能调优标准?
02 | 如何制定性能调优策略?
模块二 · Java编程性能调优 (10讲)
03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
加餐 | 推荐几款常用的性能测试工具
06 | Stream如何提高遍历集合效率?
07 | 深入浅出HashMap的设计与优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
09 | 网络通信优化之序列化:避免使用Java序列化
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
11 | 答疑课堂:深入了解NIO的优化实现原理
模块三 · 多线程性能调优 (10讲)
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
15 | 多线程调优(上):哪些操作导致了上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换?
17 | 并发容器的使用:识别不同场景下最优容器
18 | 如何设置线程池大小?
19 | 如何用协程来优化多线程业务?
20 | 答疑课堂:模块三热点问题解答
加餐 | 什么是数据的强、弱一致性?
模块四 · JVM性能监测及调优 (6讲)
21 | 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型
22 | 深入JVM即时编译器JIT,优化Java编译
23 | 如何优化垃圾回收机制?
24 | 如何优化JVM内存分配?
25 | 内存持续上升,我该如何排查问题?
26 | 答疑课堂:模块四热点问题解答
模块五 · 设计模式调优 (6讲)
27 | 单例模式:如何创建单一对象优化系统性能?
28 | 原型模式与享元模式:提升系统性能的利器
29 | 如何使用设计模式优化并发编程?
30 | 生产者消费者模式:电商库存设计优化
31 | 装饰器模式:如何优化电商系统中复杂的商品价格策略?
32 | 答疑课堂:模块五思考题集锦
模块六 · 数据库性能调优 (8讲)
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
34 | MySQL调优之事务:高并发场景下的数据库事务调优
35 | MySQL调优之索引:索引的失效与优化
36 | 记一次线上SQL死锁事故:如何避免死锁?
37 | 什么时候需要分表分库?
38 | 电商系统表设计优化案例分析
39 | 数据库参数设置优化,失之毫厘差之千里
40 | 答疑课堂:MySQL中InnoDB的知识点串讲
模块七 · 实战演练场 (4讲)
41 | 如何设计更优的分布式锁?
42 | 电商系统的分布式事务调优
43 | 如何使用缓存优化系统性能?
44 | 记一次双十一抢购性能瓶颈调优
结束语 (1讲)
结束语 | 栉风沐雨,砥砺前行!
Java性能调优实战
登录|注册

18 | 如何设置线程池大小?

刘超 2019-06-29
你好,我是刘超。
还记得我在 16 讲中说过“线程池的线程数量设置过多会导致线程竞争激烈”吗?今天再补一句,如果线程数量设置过少的话,还会导致系统无法充分利用计算机资源。那么如何设置才不会影响系统性能呢?
其实线程池的设置是有方法的,不是凭借简单的估算来决定的。今天我们就来看看究竟有哪些计算方法可以复用,线程池中各个参数之间又存在怎样的关系。

线程池原理

开始优化之前,我们先来看看线程池的实现原理,有助于你更好地理解后面的内容。
在 HotSpot VM 的线程模型中,Java 线程被一对一映射为内核线程。Java 在使用线程执行程序时,需要创建一个内核线程;当该 Java 线程被终止时,这个内核线程也会被回收。因此 Java 线程的创建与销毁将会消耗一定的计算机资源,从而增加系统的性能开销。
除此之外,大量创建线程同样会给系统带来性能问题,因为内存和 CPU 资源都将被线程抢占,如果处理不当,就会发生内存溢出、CPU 使用率超负荷等问题。
为了解决上述两类问题,Java 提供了线程池概念,对于频繁创建线程的业务场景,线程池可以创建固定的线程数量,并且在操作系统底层,轻量级进程将会把这些线程映射到内核。
线程池可以提高线程复用,又可以固定最大线程使用量,防止无限制地创建线程。当程序提交一个任务需要一个线程时,会去线程池中查找是否有空闲的线程,若有,则直接使用线程池中的线程工作,若没有,会去判断当前已创建的线程数量是否超过最大线程数量,如未超过,则创建新线程,如已超过,则进行排队等待或者直接抛出异常。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java性能调优实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(42)

  • 趙衍
    老师好!关于线程池我有一个问题一直不明白,在线程池达到了核心线程数,等待队列没满的这段时间,新的任务会被加入到等待队列。而当等待队列满了之后,最大线程数没满的这段时间,线程池会为新的任务直接创建线程。那岂不是说,我后来的任务反而比先到的任务更早被分配到线程的资源?这是不是有点不太合理呢?

    作者回复: 对的,如果队列满了,就会新增线程来执行任务,如果已经是最大线程数量,则会执行拒绝策略。

    这里不应该说不合理,而是不公平。可以深入源码查看具体的实现。

    2019-06-30
    2
    11
  • 明翼
    老师早点发这个课就好了,先回答问题:程序的总体时间是由所有部分加起来时间决定的,串行如果很慢就会严重影响性能,优化是从性能最差的地方开始的。

    请教问题:
    1)按照老师的图,如果线程没超过核心线程,就创建,超过则加入到队列,队列满又没达到最大线程则创建非核心线程,那么创建好的线程是直接执行最近来的任务那,还是从队列的头部取一个执行。
    2)第二个问题,如果线程池的线程数的在核心线程数量之内,A线程执行刚执行完任务,这时候来了个新来的任务a,那么这个A线程继续执行这个新来任务a,还是其他线程执行这个线程那,这里面有什么分配策略

    作者回复: 1、如果队列满了,这个现成的任务会创建非核心线程,也就是不会先运行队列中的任务。
    2、新来的任务a会通过创建新的线程来运行,只要线程数量小于核心线程数,新来的任务都会通过创建新的线程来运行。直到等于核心线程数,任务将会放到阻塞队列中,通过循环拿到阻塞队列中的任务执行。

    2019-07-02
    5
  • QQ怪
    老师,我想问下生产环境情况下很少碰到单个java进程在一台主机中运行,大部分肯定是多个进程同时运行,不如docker技术,都是共享同一套硬件,那这套计算方程式是不是不适用了?

    作者回复: 适用的,多个进程大部分时间不一定是重合运行的。但具体情况需要具体定,所以最终还是以压测调出来的线程数为准。

    2019-06-29
    1
    5
  • nico
    老师,请教个问题,生产环境有些应用是混部的,即一个虚拟机上跑很多个java程序,这个时候估算一个程序中的线程池的线程数,是不是就不合理了?这个要怎么估算合理的线程池配置?还有就是即使是单实例部署,cpu资源是机器内共用的,不可能只分配给java线程,这个要怎么考虑?

    作者回复: 我们先考虑单个环境,再去调优复杂环境。大多情况下,重要的服务会单独部署,尽量减少重要业务的相互影响。如果是核心业务冗余在了一个服务上,建议拆分之后分别部署。

    非核心业务,很多业务处理可能是在不同时间点,彼此相互不影响,所以不用过多考虑混合部署服务的情况。

    2019-06-29
    5
  • Jxin
    1.能理解io操作不占用cpu计算,但是io线程依旧不会让渡cpu资源的(执行时间片)。所以io线程池独立和调整io线程数只是因为它耗时不确定,一般耗时较长的特性。
    2.综上所述,那么线程池线程数基本就20-30的样子(而且这个值怎么感觉是多个线程池的线程总数呢)。那么tomcat线程池默认好像200条吧,dubbo线程池我们设置1000条线程,这是否就不合理了?(线程这块太水,麻烦老师解惑下)

    作者回复: 我们一般会调整tomcat的线程数量的,线上环境的tomcat线程数量我们一般是在16核CPU的环境下为20左右。如果有万级的并发过来,100以及1000的线程数量,可能会有问题。

    2019-07-01
    3
  • 飞翔
    话说N+1和2N 是指的核心线程数嘛? 那队列和最大线程数怎么设置呀

    作者回复: 在一些非核心业务,我们可以将核心线程数设置小一些,最大线程数量设置为计算线程数量。在一些核心业务中,两者可以设置一样。阻塞队列可以根据具体业务场景设置,如果线程处理业务非常迅速,我们可以考虑将阻塞队列设置大一些,处理的请求吞吐量会大些;如果线程处理业务非常耗时,阻塞队列设置小些,防止请求在阻塞队列中等待过长时间而导致请求已超时。

    2019-06-30
    2
  • Liam
    请教老师一个问题:

    对于应用而言,可能有多种类型的任务要执行,我们是分别创建不同的线程池还是创建一个统一的线程池来控制资源的使用呢?

    1 如果用一个统一的线程池,担心io任务占有太多线程导致其他任务没有足够的线程消费

    2 如果用多个线程池,这个资源怎么管理,会不会导致整个应用的线程数量过多,引起太多上下文切换从而导致开销过大

    作者回复: 分别创建。

    如果过分彼此相互影响,建议拆开服务,分别部署。

    2019-06-29
    2
  • 张学磊
    老师,线程池流程图提交任务后的第一个节点应该是线程数是否大于核心线程数,如果是再判断队列是否已满,否则直接创建新线程。

    思考题,个人觉得线性执行的代码会成为影响性能的关键,应尽量减少执行时间,比如减少锁持有时间,这样才能达到最大程度的并发。

    作者回复: 感谢学磊童鞋的提醒,已修正。

    答案正确!

    2019-06-29
    2
  • 杨彬Lennon
    线程池的线程分配流程,这个图不对吧?队列是否已满:是的话要执行拒绝策略,否的话要加入任务队列,等待释放的线程资源。不知道是不是这样?

    作者回复: 这个流程图没问题的,在队列满了之后,还会去判断线程池是否能再新增非核心线程,我们具体可以深入源码熟悉线程池的工作原理。

    2019-11-13
    1
  • Demon.Lee
    老师,如果线程池没有达到核心线程数,等待队列中也没有数据(即空),那么新来的任务也会创建新的线程执行么,还是用池中原有的线程执行?看流程图突然想到了这个问题

    作者回复: 没有到达核心线程数,新的任务进来也会创建线程执行,可以查看源码看到具体的实现

    2019-10-24
  • 承香墨影
    只有在 workQueue 满了之后才会创建新的线程,直到线程数到达 maximumPoolSize 值。所以这里的等待队列无法使用无界队列就会导致永远都用不上 maximumPoolSize。当我们自己指定 ThreadPoolExecutor 参数的时候,需要注意不要使用无界队列,或者使用无界队列让 corePoolSize 和 maximumPoolSize 保持一致也可以。
    参考:newFixedThreadPool 的创建过程。

    作者回复: 赞

    2019-10-09
  • 雪梨
    老师,集群部署的情况,集群有8台机器,按这说法,cpu密集型任务,也要按单机的核数+1么?

    作者回复: 一般是按单机来算线程数

    2019-09-21
  • 雪梨
    老师,cpu密集型任务,集群部署的情况,集群有8台机器,按这个N+1说法,怎么配呢?集群会影响配这个值么?
    2019-09-21
  • 疯狂咸鱼
    老师,最后一段表述是不是有问题,"将来不及处理的线程缓存到队列中",应该是任务吧

    作者回复: 对的

    2019-09-21
  • 疯狂咸鱼
    老师,平时常听到的4核8线程是什么意思?在cpu密集情况下,线程数是4+1还是8+1?

    作者回复: cpu密集情况下是n+1,如果是4核的情况下就是4+1。2n+1一般用于io密集的情况

    2019-09-21
  • 疯狂咸鱼
    老师讲课的逻辑思路真清楚,可见功力之强、知识体系完整。每天看一遍都有新的收获
    2019-09-21
  • godtrue
    课后思考及问题
    1:当程序提交一个任务需要一个线程时,会去线程池中查找是否有空闲的线程,若有,则直接使用线程池中的线程工作,若没有,会去判断当前已创建的线程数量是否超过最大线程数量,如未超过,则创建新线程,如已超过,则进行排队等待或者直接抛出异常。
    我们可以通过下面这张图来了解下线程池的线程分配流程:
    有个疑问,感觉上段文字描述和线程池的分配流程不符?请老师解释下那个更加准确,我也看到评论区有好多同学都有这样的疑惑。老师的解释是线程池的分配流程图是OK的,假如线程池的分配流程图是OK的,那请老师解释一下如下疑问:
    1:为什么会与上面这段文字的描述不符?
    2:是否意味着等待队列中的任务只会分配给,核心线程?
    3:老师后文又提到销毁线程时核心线程和非核心线程是同等对待的,这与2中的推断又是矛盾的?
    4:假如老师解释清楚了1/2/3,这三个矛盾点,那我想问一下线程池的分配流程为什么这么设计?
    5:线程池各个参数的相互关系图,我仔细看了几遍,有一个小瑕疵——corePoolSize那个位置也属于核心线程应该占的位置吧?图中核心线程有7个位置,非核心线程有6个位置,任务等待队列有9个位置,按照老师的线程池分配流程图来看,第1~6个线程提交的任务有核心线程来处理,第7~15个线程提交的任务会放入任务等待队列,第16~22个线程提交的任务会有非核心线程来处理(假设理想情况下这样)
    5-1:此时再来一个线程提交的任务会根据拒绝策略来处理?全面的拒绝策略有几种都是如何实现的?
    5-2:假如核心线程1处理完了一个任务,它去任务等待队列中获取任务是从队头有序获取的嘛?
    5-3:会存在核心线程处理完任务重新放入线程池比新提交的任务晚,导致任务被拒绝的场景吧?这个有什么解决思路嘛?
    5-4:如果只有15个线程提交任务,非核心线程是永远都不会创建了吧?
    5-5:核心线程和非核心线程,本身或者在线程池中区分不开,所以,销毁时才不做区分同等对待嘛?
    问题有点多,望老师解惑!
    2019-09-10
  • Demon.Lee
    老师,两个测试例子,一开始线程数都是2的时候,线程整体运行时间都很高,是不是因为此时线程池里面还没有线程,创建线程需要时间?

    作者回复: 是的,需要初始化

    2019-08-30
  • 风轻扬
    老师。关于线程池的拒绝策略。一共有四种
    没有您说的直接把任务缓存到队列中的策略吧?
    意思比较近的一个策略是:丢弃队列中最近的一个任务,然后缓存该任务
    望老师指点

    作者回复: 这里缓存到队列只是一个任务队列,不是一种拒绝策略,是线程池实现排队等待获取线程的策略。

    2019-08-30
  • 黎波拉小建
    请问老师 上面的时间性能测试图,IO密集和CPU密集这两张截图是什么工具跑出来的。

    作者回复: 使用的是ab测试工具

    2019-08-28
收起评论
42
返回
顶部