63

线程池ThreadPoolExecutor总结

 5 years ago
source link: https://monkeysayhi.github.io/2018/11/08/线程池ThreadPoolExecutor总结/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

之前在 源码|从串行线程封闭到对象池、线程池 中挖坑说要精炼一篇短文。本文填坑,总结线程池的种类、应用场景、ThreadPoolExecutor参数含义,最后简单介绍如何估算线程池大小。

JDK版本:oracle java 1.8.0_102

不同语言、同一语言不同库的线程池实现有差别,不要拘泥于Java这一种,没事看看work stealing等方式也挺有意思的。

三句话拿下线程池

线程池内部是一个生产者-消费者模型:

  • 用户是生产者。提交任务(task)相当于生产产品。
  • 线程池中的线程(worker)是消费者,执行任务相当于消费产品。

详见 源码|从串行线程封闭到对象池、线程池

线程池的种类

可通过Executors中的 静态工厂方法 创建不同特点的线程池,包括:

Integer.MAX_VALUE

忽略WorkStealingPool,则其他线程池底层都使用了ThreadPoolExecutor,只是参数不同。

应用场景

线程池就像水管,任务是水。

  • 如果期望水管出水的速度固定,就使用FixedThreadPool。
  • 如果期望水管出水的速度可以在水流大时增大,水流小时变小,就使用CachedThreadPool。
  • 如果期望水管出水速度恒定为1,就使用SingleThreadExecutor。
  • 如果期望水管延迟出水(延迟可控,或周期性),就使用ScheduledThreadPool。

ThreadPoolExecutor各参数的意义

Executors.defaultThreadFactory()
Executors.defaultHandler

观察corePoolSize、maximumPoolSize、workQueue三者的关系可知:

  • 如果使用有界线程池,则最好搭配有界队列,否则maximumPoolSize参数无效。
  • 相对的,CachedThreadPool被设计为一种maximumPoolSize无效的缓冲池,同时,因此,必须使用无界的同步队列,让“入队”直接变成“执行”。

线程池大小的估算

最简化公式

  • CPU 密集型应用:线程池大小设置为 N + 1
  • IO 密集型应用:线程池大小设置为 2N

公式的意义在于 避免陷入极端情况 。其中,计算密集型任务假设“ 等待时间/计算时间 ”等于0,IO密集型任务假设“ 等待时间/计算时间 ”等于1。

为什么要有+1呢?

这是因为, 就算是计算密集型任务,也可能存在缺页等问题(需要了解虚拟内存和物理内存的分配),产生“隐式”的IO 。多一个额外的线程能确保CPU时钟周期不会被浪费,又不至于增加太多线程调度成本。

严格公式:

假设每个线程的“ 等待时间 / 计算时间 ”大小相等,显然,“ 计算时间 / (计算时间 + 等待时间) ”也相等。对1个线程而言,只有计算时间占用了 逻辑CPU ,假设这个线程一直运行在同1个逻辑CPU上,显然,该逻辑CPU的 CPU利用率 即等于“ 计算时间 / (计算时间 + 等待时间) ”。

对于多个线程的情况是一样的,则有公式:

逻辑CPU数 * CPU利用率 / 线程数 = 计算时间 / (计算时间 + 等待时间)

倒腾倒腾,得到:

线程数 = (1 + 等待时间/计算时间) * 逻辑CPU数 * CPU利用率
  • 1 + 等待时间/计算时间 ” 只与任务本身有关。
  • 逻辑CPU数可通过 cat /proc/cpuinfo | grep -c processor 得到。
  • 标准的CPU利用率 要通过实际监控得到,但在估算线程池大小时,应看做“ 期望得到的CPU利用率 ”,即 可分配给该任务的CPU比例 。如果只打算分配一半CPU给任务的话,就是0.5。

如果估算得到的线程数比较多,那么还要适当提高可分配的CPU比例,因为线程切换的成本随线程数增加而增加。如果竞争较激烈,则可以适当降低可分配的CPU比例,因为竞争通常也会导致线程阻塞,使CPU空闲。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK