深入拆解Tomcat & Jetty
李号双
eBay技术主管
立即订阅
6067 人已学习
课程目录
已完结 44 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | Java程序员如何快速成长?
免费
模块一 必备基础 (4讲)
01 | Web容器学习路径
02 | HTTP协议必知必会
03 | 你应该知道的Servlet规范和Servlet容器
04 | 实战:纯手工打造和运行一个Servlet
模块二 整体架构 (9讲)
05 | Tomcat系统架构(上): 连接器是如何设计的?
06 | Tomcat系统架构(下):聊聊多层容器的设计
07 | Tomcat如何实现一键式启停?
08 | Tomcat的“高层们”都负责做什么?
09 | 比较:Jetty架构特点之Connector组件
10 | 比较:Jetty架构特点之Handler组件
11 | 总结:从Tomcat和Jetty中提炼组件化设计规范
12 | 实战:优化并提高Tomcat启动速度
13 | 热点问题答疑(1):如何学习源码?
模块三 连接器 (9讲)
14 | NioEndpoint组件:Tomcat如何实现非阻塞I/O?
15 | Nio2Endpoint组件:Tomcat如何实现异步I/O?
16 | AprEndpoint组件:Tomcat APR提高I/O性能的秘密
17 | Executor组件:Tomcat如何扩展Java线程池?
18 | 新特性:Tomcat如何支持WebSocket?
19 | 比较:Jetty的线程策略EatWhatYouKill
20 | 总结:Tomcat和Jetty中的对象池技术
21 | 总结:Tomcat和Jetty的高性能、高并发之道
22 | 热点问题答疑(2):内核如何阻塞与唤醒进程?
模块四 容器 (8讲)
23 | Host容器:Tomcat如何实现热部署和热加载?
24 | Context容器(上):Tomcat如何打破双亲委托机制?
25 | Context容器(中):Tomcat如何隔离Web应用?
26 | Context容器(下):Tomcat如何实现Servlet规范?
27 | 新特性:Tomcat如何支持异步Servlet?
28 | 新特性:Spring Boot如何使用内嵌式的Tomcat和Jetty?
29 | 比较:Jetty如何实现具有上下文信息的责任链?
30 | 热点问题答疑(3):Spring框架中的设计模式
模块五 通用组件 (4讲)
31 | Logger组件:Tomcat的日志框架及实战
32 | Manager组件:Tomcat的Session管理机制解析
33 | Cluster组件:Tomcat的集群通信原理
特别放送 | 如何持续保持对学习的兴趣?
模块六 性能优化 (8讲)
34 | JVM GC原理及调优的基本思路
35 | 如何监控Tomcat的性能?
36 | Tomcat I/O和线程池的并发调优
37 | Tomcat内存溢出的原因分析及调优
38 | Tomcat拒绝连接原因分析及网络优化
39 | Tomcat进程占用CPU过高怎么办?
40 | 谈谈Jetty性能调优的思路
41 | 热点问题答疑(4): Tomcat和Jetty有哪些不同?
结束语 (1讲)
结束语 | 静下心来,品味经典
深入拆解Tomcat & Jetty
登录|注册

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

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

Java 线程池

简单的说,Java 线程池里内部维护一个线程数组和一个任务队列,当任务处理不过来的时,就把任务放到队列里慢慢处理。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Tomcat & Jetty 》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(28)

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

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

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

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

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

    作者回复: offer方法返回false表示添加失败,添加失败就会创建新线程。

    TaskQueue的父类总是返回true,但是TaskQueue就不会总是返回true了,可能是false,区别在这里。

    2019-07-17
    1
  • calljson
    空闲线程到队列取任务,能否讲解下原理,最好附上源码,谢谢
    2019-07-02
    1
  • 13963865700
    老师,您好,请问:
    1.Tomcat在默认队列长度无限制的情况下,是不是不会触发拒绝策略,即使线程数达到maxQueueSize也一直把任务放队列中?
    2.这种情况会不会拖垮Tomcat,发生内存溢出?

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

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

    作者回复: 对的

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

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

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

    作者回复: 1.可以调用ThreadPoolExecutor的这个方法来指定是否回收核心线程:
    public void allowCoreThreadTimeOut(boolean value)

    2.kill进程会立即退出,内核会负责清理这个进程的所有资源。

    2019-06-18
    1
  • Liam
    可以用atomic原子变量替换锁吧
    2019-06-18
    1
    1
  • ylw666
    如果总线程数达到 maximumPoolSize,执行拒绝策略
    ======是队列满才执行拒绝策略吧,不是总线程数达到 maximumPoolSize执行拒绝策略,难道是我理解错了?
    2019-11-04
  • 小美
    tomcat线程池和jdk线程池使用场景上有什么区别呢老师?因为正常思维阻塞队列是做一个缓冲,为什么jdk要使用这么一种违反常理的设计?
    2019-10-17
  • 吴大山
    Tomcat 线程池扩展了原生的 ThreadPoolExecutor,通过重写 execute 方法实现了自己的任务处理逻辑:
    1. xxx
    2. 再来任务的话,就把任务添加到任务队列里让所有的线程去抢,如果队列满了就创建临时线程。
    3. xxx
    4. xxx

    我细看了一下第二步代码:
    方法定位:org.apache.tomcat.util.threads.TaskQueue#offer
    逻辑定位:if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;

    这么看逻辑好像是:再来任务的话,如果线程数少于maximumPoolSize时,都会优先使用线程,而不会入队
    2019-10-14
  • Geek_00d567
    getPoolSize,加了重入锁ReentrantLock。可以考虑信号量做控制。
    2019-09-19
  • jaryoung
    课后题,获取一次设置成一个局部变量,局部变量属于线程安全,无惧
    2019-08-28
  • 楊_宵夜
    李老师好,getPoolSize()不是动态变化的吗?如果调用一次够缓存起来,缓存的值是不是会存在一致性问题呢?
    2019-08-26
  • Wiggle Wiggle
    CachedThreadPool 中 Cached 有什么意思?看它定制的 ThreadPool 并没有 cached 的感觉
    2019-08-26
  • 星辰
    结合老师的讲解去看,印象会更深刻,脉络和细节点也会变得清晰,给老师点赞!

    作者回复: 谢谢

    2019-08-13
  • brianway
    所以通过重写TaskQueue的offer方法,达到的效果就是将无界队列变成了有界队列,且队列长度限制=当前线程数,不知道我理解的对不对。举个例子,corePoolSize=4,maximumPoolSize=10,那来一个任务起一个线程,直到线程数为4,然后再来的任务就入队列,如果队列里任务积累到4个,随着任务继续增多,会新起线程处理,队列长度限制也会依次变成5,6,7,8,一直到10。
    2019-08-07
    1
  • HARDMAN
    请教老师,如果线程池已满,任务队列也满了,那么tomcat会拒绝后面的请求,这时如何进一步增强tomcat的处理能力,让它能同时处理更多请求呢?

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

    2019-07-06
收起评论
28
返回
顶部