首页
学习
活动
专区
工具
TVP
发布

Java性能权威指南:线程池与ThreadPoolExecutor

在Java中,线程可以使用定制的代码来管理;应用也可以利用线程池。Java EE应用服 务器就是围绕用一个或多个线程池处理请求这一概念构建的:对服务器上 Servlet 的每个 调用都是通过池中的线程处理的(也有可能不同)。类似地,其他应用可以使用 Java 的 ThreadPoolExecutor 并行执行任务。

设置最大线程数

假设 JVM 有 4 个 CPU 可用,最大线程数至少要设置为 4,假设机器有4个CPU,使用不 同的线程数测试,具体的性能数据见表 9-1

设置最小线程数

将最小线程数设置为其他某个值(比如 1),出发点是防止系统创建太多线程,以节省系统 资源。因为每个线程都需要一定量的内存,特别是线程的栈。

线程池任务大小

等待线程池来执行的任务会被保存到某类队列或列表中;当池中有线程可以执行任务时, 就从队列中拉出一个。这会导致不均衡:队列中任务的数量有可能变得非常大。如果队列 太大,其中的任务就必须等待很长时间,直到前面的任务执行完毕。

举例而言,假 设某个应用服务器的任务队列中有30 000个任务,有4个CPU可用,如果执行一个任务 只需要 50 毫秒,同时假设这段时间不会到达新任务,则清空任务队列需要 6 分钟。这可 能是可以接受的,但如果每个任务需要 1 秒钟,则清空任务队列需要 2 小时。

不管是哪种情况,如果达到了队列数限制,再添加任务就会失败。ThreadPoolExecutor 有 一 个 rejectedExecution 方 法, 用 于 处 理 这 种 情 况( 默 认 会 抛 出 RejectedExecutionException)。应用服务器会向用户返回某个错误:或者是 HTTP 状态码 500(内部错误),或者是 Web 服务器捕获错误,并向用户给出合理的解释消息——其中后 者是最理想的。

设置ThreadPoolExecutor的大小

线程池的一般行为是这样的:创建时准备好最小数目的线程,如果来了一个任务,而此时 所有的线程都在忙碌,则启动一个新线程(一直到达到最大线程数),任务就可以立即执 行了。否则,任务被加入等待队列,如果任务队列中已经无法加入新任务,则拒绝之。不 过,ThreadPoolExecutor 的表现可能和这种标准行为有点不同。

根据所选任务队列的类型,ThreadPoolExecutor 会决定何时启动一个新线程。有以下 3 种 可能。

SynchronousQueue

如果 ThreadPoolExecutor 搭配的是 SynchronousQueue,则线程池的行为会和我们预计的 一样,它会考虑线程数:如果所有的线程都在忙碌,而且池中的线程数尚未达到最大,则新任务会启动一个新线程。然而,这个队列没办法保存等待的任务:如果来了一个任 务,创建的线程数已经达到最大值,而且所有线程都在忙碌,则新的任务总是会被拒 绝。所以如果只是管理少量的任务,这是个不错的选择;但是对于其他情况,就不合适 了。该类文档建议将最大线程数指定为一个非常大的值,如果任务完全是 CPU 密集型 的,这可能行得通,但是我们会看到,其他情况下可能会适得其反。另一方面,如果需 要一个容易调整线程数的线程池,这种选择会更好。

无界队列

如果 ThreadPoolExecutor 搭配的是无界队列(比如 LinkedBlockedingQueue),则不会拒 绝任何任务(因为队列大小没有限制)。这种情况下,ThreadPoolExecutor 最多仅会按 最小线程数创建线程,也就是说,最大线程池大小被忽略了。如果最大线程数和最小线 程数相同,则这种选择和配置了固定线程数的传统线程池运行机制最为接近。

有界队列

在决定何时启动一个新线程时,使用了有界队列(如 ArrayBlockingQueue)的 ThreadPoolExecutor 会采用一个非常复杂的算法。比如,假设池的核心大小为 4,最大 为 8,所用的 ArrayBlockingQueue 最大为 10。随着任务到达并被放到队列中,线程池中 最多会运行 4 个线程(也就是核心大小)。即使队列完全填满,也就是说有 10 个处于等 待状态的任务,ThreadPoolExecutor 也是只利用 4 个线程。

如果队列已满,而又有新任务加进来,此时才会启动一个新线程。这里不会因为队列已满而拒绝该任务,相反,会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间。

快速小结

1. 有时对象池也是不错的选择,线程池就是情形之一:线程初始化的成本 很高,线程池使得系统上的线程数容易控制。

2. 线程池必须仔细调优。盲目向池中添加新线程,在某些情况下对性能会 有不利影响。

3. 在使用ThreadPoolExecutor时,选择更简单的选项通常会带来最好的、 最能预见的性能。

《Java性能权威指南》文字版PDF下载,关注:java技艺,回复:Java性能权威指南

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180713G1MNLR00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券