(点击上方的蓝色文字,可快速关注我们)

在阅读研究线程池的源码之前,一直感觉线程池是一个框架中最高深的技术。研究后才发现,线程池的实现是如此精巧。本文从技术角度分析了线程池的本质原理和组成,同时分析了JDK、Jetty6、Jetty8、Tomcat的源码实现,对于想了解线程池本质、更好的使用线程池或者定制实现自己的线程池的业务场景具有一定指导意义。

使用线程池的意义

复用:类似WEB服务器等系统,长期来看内部需要使用大量的线程处理请求,而单次请求响应时间通常比较短,此时Java基于操作系统的本地调用方式大量的创建和销毁线程本身会成为系统的一个性能瓶颈和资源浪费。若使用线程池技术可以实现工作线程的复用,即一个工作线程创建和销毁的生命周期期间内可以执行处理多个任务,从而总体上降低线程创建和销毁的频率和时间,提升了系统性能。

流控:服务器资源有限,超过服务器性能的过高并发设置反而成为系统的负担,造成CPU大量耗费于上下文切换、内存溢出等后果。通过线程池技术可以控制系统最大并发数和最大处理任务量,从而很好的实现流控,保证系统不至于崩溃。

功能:JDK的线程池实现的非常灵活,并提供了很多功能,一些场景基于功能的角度会选择使用线程池。

线程池技术要点

从内部实现上看,线程池技术可主要划分为如下6个要点实现:

工作者线程worker:即线程池中可以重复利用起来执行任务的线程,一个worker的生命周期内会不停的处理多个业务job。线程池“复用”的本质就是复用一个worker去处理多个job,“流控“的本质就是通过对worker数量的控制实现并发数的控制。通过设置不同的参数来控制worker的数量可以实现线程池的容量伸缩从而实现复杂的业务需求。

待处理工作job的存储队列:工作者线程workers的数量是有限的,同一时间最多只能处理最多workers数量个job。对于来不及处理的job需要保存到等待队列里,空闲的工作者work会不停的读取空闲队列里的job进行处理。基于不同的队列实现,可以扩展出多种功能的线程池,如定制队列出队顺序实现带处理优先级的线程池、定制队列为阻塞有界队列实现可阻塞能力的线程池等。流控一方面通过控制worker数控制并发数和处理能力,一方面可基于队列控制线程池处理能力的上限。

线程池初始化:即线程池参数的设定和多个工作者workers的初始化。通常有一开始就初始化指定数量的workers或者有请求时逐步初始化工作者两种方式。前者线程池启动初期响应会比较快但造成了空载时的少量性能浪费,后者是基于请求量灵活扩容但牺牲了线程池启动初期性能达不到最优。

处理业务job算法:业务给线程池添加任务job时线程池的处理算法。有的线程池基于算法识别直接处理job还是增加工作者数处理job或者放入待处理队列,也有的线程池会直接将job放入待处理队列,等待工作者worker去取出执行。

workers的增减算法:业务线程数不是持久不变的,有高低峰期。线程池要有自己的算法根据业务请求频率高低调节自身工作者workers的数量来调节线程池大小,从而实现业务高峰期增加工作者数量提高响应速度,而业务低峰期减少工作者数来节省服务器资源。增加算法通常基于几个维度进行:待处理工作job数、线程池定义的最大最小工作者数、工作者闲置时间。

线程池终止逻辑:应用停止时线程池要有自身的停止逻辑,保证所有job都得到执行或者抛弃。

结合上面的技术点,列举几种线程池实现方式。

工作者workers与待处理工作队列实现方式举例

线程池初始化与处理业务job算法举例

JDK

线程池构造与工作者初始化:1. 基于多个构造参数实现灵活初始化,几个核心参数如下:corePoolSize:核心工作者数,maximumPoolSize:最大工作者数,keepAliveTime:超过核心工作者数时闲置工作者的存活时间。workQueue:待处理job队列,即前面提到的BlockingQueue接口。2. 默认初始化后不启动工作者,等待有请求时才启动。可以通过调用线程池接口提前启动核心工作数个工作者线程,也可以启动业务期望的多个工作者线程。

处理业务job的算法:1. 工作者workers数量低于核心工作者数corePoolSize时会优先创建一个工作者worker处理job,处理成功则返回。2. 工作者workers数量高于核心工作者数时会优先把job放入到待处理队列,放入队列成功时处理结束。3. 步骤2中入队失败会识别工作者数是否还小于最大工作者数maximumPoolsize,小于的话也会新创建一个工作者worker处理job。4. 拒绝处理。

Jetty6

线程池构造与工作者初始化:1. 同样支持设置多个参数:_spawnOrShrinkAt:扩容/缩容阀值,_minThreads:最小工作者数,_maxThreads:最大工作者数,_maxIdleTimeMs:闲置工作者最大闲置超时时间,2. 初始化后直接启动_minThreads个工作者线程。

处理业务job的算法:1. 查找闲置的工作者worker,找到则派发job。2. 没有闲置的工作者,将job存入待处理数组。3. 当识别到数组中待处理job超过扩容阀值参数时,扩容增加工作者处理job。4. 否则不处理。

Jetty8

线程池构造与工作者初始化:1. 配置参数类似Jetty6,去除了_spawnOrShrinkAt阀值参数。2. 初始化后直接启动_minThreads个工作者线程。

处理业务job的算法:非常简单,直接将待处理job入队。

Tomcat

线程池构造与工作者初始化:1. 基于JDK线程池的构造方法。2. 来请求时启动工作者。

处理业务job的算法:处理方法复用JDK的,但是在开始提交前扩展了JDK的功能,实现了可以统计提交数submittedCount的能力。

线程池工作者worker的增减机制举例

JDK

工作者增加算法:1. 待处理job来时,工作者workers数量低于核心工作者数corePoolSize时。2. 待处理job来时,workers数超过核心数小于最大工作者数且入待处理队列失败场景。3. 业务调用线程池的更新核心工作者数接口时,若发现扩容,会增加工作者数。

工作者减少算法:1. 待处理任务队列里没有job并且工作者workers数量超过了核心工作者数corePoolSize。2. 待处理任务队列里没有job并且允许工作者数量小于核心工作者参数为true,此场景会至少保留一个工作者线程。

Jetty6

工作者增加算法:1. 启动线程池时会启动_minThreads个工作者线程。2. 待处理的job数量高于了阀值参数且工作者数没有达到最大值时会增加工作者。3. 调用线程池接口setMinThreads更新最小工作者数时会根据需要增加工作者。

工作者减少算法:如下三个条件同时满足时会减少工作者:1. 待处理任务数组中没有待处理job。2. 工作者workers数量超过了最小工作者数_minThreads。3. 闲置工作者线程数高于了阀值参数。


欢迎关注“互联网架构师”,我们分享最有价值的互联网技术干货文章,助力您成为有思想的全栈架构师,只聊架构,不聊其他!打造最有价值的架构师圈子和社区。

长按下方的二维码可以快速关注我们

如想加群讨论学习,请点击右下角的“加群学习”菜单入群

几种线程池的实现算法分析相关推荐

  1. Java ExecutorService四种线程池的例子与说明

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() {@Overridepublic void run() {// ...

  2. java常用的几种线程池

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  3. Executors创建的4种线程池的使用

    Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFi ...

  4. 请回答一下Java中有几种线程池及实现过程?

    请回答一下Java中有几种线程池及实现过程? 1.newFixedThreadPool创建一个指定工作线程数量的线程池.每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将 ...

  5. Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor...

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() {@Override public void run ...

  6. Android AsyncTask两种线程池分析和总结

    转自:http://bbs.51cto.com/thread-1114378-1-1.html Android AsyncTask两种线程池分析和总结 (一)    前言 在android Async ...

  7. java中四种线程池及poolSize、corePoolSize、maximumPoolSize

    目录 ThreadPoolExecutor重要参数 poolSize.corePoolSize.maximumPoolSize 四种线程池 newFixedThreadPool newCachedTh ...

  8. java中四种线程池的区别

    本文按: 一. 线程池的使用 二. 几种线程池的区别 三. 如何合理配置线程池 一.线程池的使用 在Java中,通常使用Executors 获取线程池.常用的线程池有以下几种: (1)CachedTh ...

  9. Java通过Executors提供四种线程池

    http://cuisuqiang.iteye.com/blog/2019372 Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如 ...

  10. [转]new Thread的弊端及Java四种线程池的使用

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...

最新文章

  1. 一文读懂图像局部特征点检测算法
  2. JSON.parse()出错解决
  3. $digest already in progress 解决办法——续
  4. python语言怎么学-如何学习Python,以及新手如何入门?
  5. Android-Activity启动流程
  6. 单例模式【SingletonPattern】
  7. 开源最大的谎言是什么?
  8. 锐炬显卡可以linux吗,Intel Broadwell桌面CPU性能测试:Iris Pro 6200核显无敌了
  9. redis——redis持久化处理
  10. mysql 8.0 postgresql_PostgreSQL8.0的安装和配置- -
  11. Linux内核等待队列wait_queue学习
  12. linux实现快捷键,Linux Bash下如何实现快捷键效果
  13. 复化梯形公式求积算法 c语言,c语言版本复合梯形法、辛普森法求积公式
  14. p3369跳表代替平衡树
  15. storm 阿姆歌曲_Eminem经典歌词
  16. CSS设置文本、段落样式
  17. MySQL存储IP字段类型
  18. 织梦标签全攻略[转]
  19. 企业微信认证上网推广的好处
  20. 阿里云天池供应链大赛(一)

热门文章

  1. SQL Server存储过程同时返回分页结果集和总数
  2. mongodb常用命令脚本化-自动化运维
  3. baidu__git_android
  4. 可爱的 CreateMessageDialog
  5. [Node] 基础知识
  6. vc ++ 如何做界面开发?
  7. 大型互联网网站架构心得之一:分
  8. hdu 3853 LOOPS 概率dp入门题
  9. C++经典书籍推荐 .
  10. Illustrator中文版教程,如何在 Illustrator 中使用颜色混合器创建色板?