深入拆解 Tomcat & Jetty
李号双
eBay 技术主管
38890 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
深入拆解 Tomcat & Jetty
15
15
1.0x
00:00/00:00
登录|注册

17 | Executor组件:Tomcat如何扩展Java线程池?

任务处理逻辑
构造函数参数
定制版的任务队列
定制版的ThreadPoolExecutor
FixedThreadPool/CachedThreadPool
ThreadPoolExecutor
线程池
对象池
课后思考
本期精华
Tomcat线程池
Java线程池
池的概念
文章

该思维导图由 AI 生成,仅供参考

在开发中我们经常会碰到“池”的概念,比如数据库连接池、内存池、线程池、常量池等。为什么需要“池”呢?程序运行的本质,就是通过使用系统资源(CPU、内存、网络、磁盘等)来完成信息的处理,比如在 JVM 中创建一个对象实例需要消耗 CPU 和内存资源,如果你的程序需要频繁创建大量的对象,并且这些对象的存活时间短,就意味着需要进行频繁销毁,那么很有可能这部分代码会成为性能的瓶颈。
而“池”就是用来解决这个问题的,简单来说,对象池就是把用过的对象保存起来,等下一次需要这种对象的时候,直接从对象池中拿出来重复使用,避免频繁地创建和销毁。在 Java 中万物皆对象,线程也是一个对象,Java 线程是对操作系统线程的封装,创建 Java 线程也需要消耗系统资源,因此就有了线程池。JDK 中提供了线程池的默认实现,我们也可以通过扩展 Java 原生线程池来实现自己的线程池。
同样,为了提高处理能力和并发度,Web 容器一般会把处理请求的工作放到线程池里来执行,Tomcat 扩展了原生的 Java 线程池,来满足 Web 容器高并发的需求,下面我们就来学习一下 Java 线程池的原理,以及 Tomcat 是如何扩展 Java 线程池的。

Java 线程池

简单的说,Java 线程池里内部维护一个线程数组和一个任务队列,当任务处理不过来的时,就把任务放到队列里慢慢处理。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Tomcat的线程池是一个定制版的ThreadPoolExecutor,通过扩展Java原生线程池实现了对线程池的扩展,以满足Web容器高并发的需求。Tomcat的线程池对线程数和任务队列长度都进行了限制,定制了自己的任务处理流程。它重写了execute方法,实现了在总线程数达到最大数时,尝试向任务队列添加任务,添加失败后再执行拒绝策略。此外,Tomcat还实现了定制版的任务队列TaskQueue,重写了offer方法,使得在任务队列长度无限制的情况下,线程池仍然有机会创建新的线程。通过这些定制化的实现,Tomcat的线程池能够更好地适应高并发环境,提高程序的性能和并发处理能力。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Tomcat & Jetty 》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(47)

  • 最新
  • 精选
  • 永光
    观察 Tomcat 线程池和 Java 原生线程池的区别,其实就是在第 3 步,Tomcat 在线程总数达到最大数时,不是立即执行拒绝策略,而是再尝试向任务队列添加任务,添加失败后再执行拒绝策略。 问题: 感觉这两种方式都一样呀,前corePoolSize都是直接创建线程来处理。后续都是先放在队列里面,满了在创建临时线程来处理。 Tomcat线程池,在达到max时 再次检测,并尝试插入队列有什么意义呢?我理解再次检测队列也是满的呀? 2、

    作者回复: 有可能第一次尝试放队列是满的,失败,再尝试创建临时线程,也满了,但是这个过程中,队列中的任务可能被临时线程消费了一部分,再往队列中送可能会成功。

    2019-06-18
    8
    30
  • 世纪猛男
    关于今日的思考题 getPoolSize. 用Volatile去修饰一个变量不可行,因为变更过程,会基于之前的pool size,无法做到原子操作。 用atomic 也不合适 并发量高的时候 会导致 大量的更新失败, 持续消耗CPU。 所以还不如加锁来的痛快。 请教老师的想法

    作者回复: 可以加锁,但是没有必要多次调用,调一次把结果存起来就行。

    2019-06-18
    7
    9
  • 吃饭饭
    老师,TaskQueue 重写了 offer 方法的关键是什么?是 TaskQueue(int capacity) ,只是把无界变有界了吗?每台看明白 offer 具体的改变是什么

    作者回复: offer方法返回false表示添加失败,添加失败就会创建新线程。 TaskQueue的父类总是返回true,但是TaskQueue就不会总是返回true了,可能是false,区别在这里。

    2019-07-17
    5
    7
  • 迎风劲草
    老师,核心线程如果超过keeplive时间,是否也会回收?还有如果我的队列中还有等待执行的runable,这时候kill 进程,时候需要等到所有runable被执行要,进程才结束吗?

    作者回复: 1.可以调用ThreadPoolExecutor的这个方法来指定是否回收核心线程: public void allowCoreThreadTimeOut(boolean value) 2.kill进程会立即退出,内核会负责清理这个进程的所有资源。

    2019-06-18
    5
  • 13963865700
    老师,您好,请问: 1.Tomcat在默认队列长度无限制的情况下,是不是不会触发拒绝策略,即使线程数达到maxQueueSize也一直把任务放队列中? 2.这种情况会不会拖垮Tomcat,发生内存溢出?

    作者回复: 为了解决这个问题,Tomcat的定制版任务队列TaskQueue 重写了 LinkedBlockingQueue 的 offer 方法,在合适的时机返回 false,返回 false 表示任务添加失败,这时线程池会创建新的线程。

    2019-06-26
    2
    4
  • -W.LI-
    李老师好。我有个问题,原生队列是在队列满时新建线程处理。然后当线程达到最大线程数的时候,不就是队列已满,线程也开满了么。Tomcat补获异常后再往队列里放一次,只是为了做后的努力争取不丢任务么?

    作者回复: 对的

    2019-06-20
    4
  • z.l
    感觉直接读workers.size()就可以了么,因为创建线程和销毁线程的方法都加锁了,而且是同一把锁,不懂为啥getPoolSize()方法还要额外加锁?

    作者回复: 是的,这个地方Tomcat的实现可以简化。

    2019-06-18
    3
    4
  • HARDMAN
    请教老师,如果线程池已满,任务队列也满了,那么tomcat会拒绝后面的请求,这时如何进一步增强tomcat的处理能力,让它能同时处理更多请求呢?

    作者回复: 只能提高web应用的处理速度,否则只能排队,可以把队列长度加大,不推荐这样做,因为客户端需要等较长时间,个人感觉还不如直接返回错误

    2019-07-06
    3
  • yang
    结合老师的讲解去看,印象会更深刻,脉络和细节点也会变得清晰,给老师点赞!

    作者回复: 谢谢

    2019-08-13
  • 陆离
    corePoolSize的有什么设置的策略吗? 需要和CPU个数联系起来吗?

    作者回复: 如何是纯粹的CPU密集型应用,corepoolsize可以设置为CPU核数

    2019-06-18
收起评论
显示
设置
留言
47
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部