`
softtian1983
  • 浏览: 185116 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Tomcat 线程池简介

阅读更多

      目前市场上常用的开源Java Web容器有Tomcat、Resin和Jetty。其中Resin从V3.0后需要购买才能用于商业目的,而其他两种则是纯开源的。可以分别从他们的网站上下载最新的二进制包和源代码。  作为Web容器,需要承受较高的访问量,能够同时响应不同用户的请求,能够在恶劣环境下保持较高的稳定性和健壮性。在HTTP服务器领域,Apache HTTPD的效率是最高的,也是最为稳定的,但它只能处理静态页面的请求,如果需要支持动态页面请求,则必须安装相应的插件,比如mod_perl可以处理Perl脚本,mod_python可以处理Python脚本。

       上面介绍的三中Web容器,都是使用Java编写的HTTP服务器,当然他们都可以嵌到Apache中使用,也可以独立使用。分析它们处理客户请求的方法有助于了解Java多线程和线程池的实现方法,为设计强大的多线程服务器打好基础。

      Tomcat是使用最广的Java Web容器,功能强大,可扩展性强。最新版本的Tomcat(5.5.17)为了提高响应速度和效率,使用了Apache Portable Runtime(APR)作为最底层,使用了APR中包含Socket、缓冲池等多种技术,性能也提高了。APR也是Apache HTTPD的最底层。可想而知,同属于ASF(Apache Software Foundation)中的成员,互补互用的情况还是很多的,虽然使用了不同的开发语言。 Tomcat 的线程池位于tomcat-util.jar文件中,包含了两种线程池方案。方案一:使用APR的Pool技术,使用了JNI;方案二:使用Java实现的ThreadPool。这里介绍的是第二种。如果想了解APR的Pool技术,可以查看APR的源代码。

       ThreadPool默认创建了5个线程,保存在一个200维的线程数组中,创建时就启动了这些线程,当然在没有请求时,它们都处理“等待”状态(其实就是一个while循环,不停的等待notify)。如果有请求时,空闲线程会被唤醒执行用户的请求。

具体的请求过程是: 服务启动时,创建一个一维线程数组(maxThread=200个),并创建空闲线程(minSpareThreads=5个)随时等待用户请求。 当有用户请求时,调用 threadpool.runIt(ThreadPoolRunnable)方法,将一个需要执行的实例传给ThreadPool中。其中用户需要执行的实例必须实现ThreadPoolRunnable接口。 ThreadPool 首先查找空闲的线程,如果有则用它运行要执行ThreadPoolRunnable;如果没有空闲线程并且没有超过maxThreads,就一次性创建minSpareThreads个空闲线程;如果已经超过了maxThreads了,就等待空闲线程了。总之,要找到空闲的线程,以便用它执行实例。找到后,将该线程从线程数组中移走。 接着唤醒已经找到的空闲线程,用它运行执行实例(ThreadPoolRunnable)。 运行完ThreadPoolRunnable后,就将该线程重新放到线程数组中,作为空闲线程供后续使用。 ThreadPool提供的仅仅是线程池的实现,而如何使用线程池也是有很大学问的。让我们看看Tomcat是如何使用ThreadPool的吧。

      Tomcat有两种EndPoint,分别是AprEndpoint和PoolTcpEndpoint。前者自己实现了一套线程池(其实这和Tomcat 老版本的方案是相同的,至今Tomcat中还保留着老版本的线程池,PoolTcpEndpoint也有类似的代码,通过“策略”可以选择不同的线程池方案)。我们只关注PoolTcpEndpoint如何使用ThreadPool的。首先,PoolTcpEndpoint创建了一个ThreadPoolRunnable实例——LeaderFollowerWorkerThread,实际上该实例就是接收(Accept)并处理(Process)用户socket请求。接着将该实例放进ThreadPool中并运行,此时就可以接收用户的请求了。 当有Socket请求时,LeaderFollowerWorkerThread首先获得了Socket实例,注意此时LeaderFollowerWorkerThread并没有急着处理该Socket,而是在响应Socket消息前,再次将LeaderFollowerWorkerThread放进ThreadPool中,从而它(当然是另外一个线程了)可以继续处理其他用户的Socket请求;接着,拥有Socket的LeaderFollowerWorkerThread再来处理该用户的Socket请求。整个过程与传统的处理用户Socket请求是不同的,也和Tomcat老版本不同。传统的处理方法是:有一个后台运行的监听线程负责统一处理接收(注意只是“接收”)Socket请求,当有新的Socket请求时,将它赋值给一个Worker线程(通常是唤醒该线程),并有后者处理Socket请求,监听线程继续等待其他Socket请求。所以整个过程中有一个从Listener到Worker切换的过程。 而新版本Tomcat很有创造性的使用了另外一种方法,正如前文所描述的,接收和处理某个用户Socket请求的始终是由一个线程全程负责,没有切换到其他线程处理,少了这种线程间的切换是否更有效率呢?我还不能确认。不过这种使用方式确实有别于传统模式,有种耳目一新的感觉 由此可以看出,Tomcat的线程池实现是比较简单的,ThreadPool.java也只有840行代码。用一个一维数组保存空闲的线程,每次以一个较小步伐(5个)创建空闲线程并放到线程池中。使用时从数组中移走空闲的线程,用完后,再“归还”给线程池。

 

       除了Tomcat外,Jetty是另外一个重要的Java Web容器,号称“最小的”Web容器,从Jetty的源代码规模可以看出它确实比较小。而且它的ThreadPool的实现也非常简单,整个代码ThreadPool代码只有450行左右,可见小巧之极。

ThreadPool代码位于com.mortbty.thread包中,其中最重要的方法是dispatch()和内部类PoolThread。顾名思义,dispatch方法主要是将Runnable实例派给线程池中的空闲PoolThread,由后者运行Runnable。
      还是看看整个过程吧。首先,ThreadPool创建_minThreads个空闲PoolThread,并把它们添加到空闲线程队列中。当需要运行 Runnable时,首先查找是否有空闲的PoolThread,如果有空闲的,这由它处理;如果没有并且PoolThread并没有超过 _maxThreads个时,则创建一个新的PoolThread,并由这个新创建的PoolThread运行Runnable;如果 PoolThread超过了_maxThreads,则一直等待有空闲的PoolThread出现。在PoolThread运行之前,必须把该 PoolThread从空闲线程队列中移走。再来看看PoolThread的实现吧。和所有的Worker线程一样,用一个while(flag){wait();}循环等待Runnable的到来,当有Runnable被ThreadPool.dispatch()时,该PoolThread就运行Runnable;当运行完成后,再“归还”给空闲线程队列。
      Jetty如何使用ThreadPool?整个Jetty只使用了一个ThreadPool实例,具体入口在 org.mortbay.jetty.Server中被实例化的,Connector中也使用Server的ThreadPool处理用户的Socket 请求。Connector是处理用户Socket请求的入口,一个Connector创建_acceptors个Acceptor,由Acceptor处理用户Socket请求时,当有Socket请求时,就创建一个Connection放到线程池中处理,而Acceptor继续处理其他的Socket请求。这是个传统的Listener和Worker处理方式。  在这些Java Web容器中,Resin算得上很特别的,小巧稳定,而且效率很高。在这些Java Web容器中,算它的效率最高了。很多大型的网站中都能找到它的身影。Resin从3.0版本后开始走“特色”的开源路,与MySql很相似——如果用于商业目的,则需要买它的License。但对于个人研究而言,这已经不错了,在网站上可以下载除了涉及License的源代码外其他所有代码。

      说Resin特别,还主要是由于它的性能出众,即使在很多企业级应用中也能派上用场。Resin的数据库连接池做的很不错,效率非常高。不过这里我们讨论它的线程池,看看有何特别之处。
      Resin的ThreadPool位于com.caucho.util.ThreadPool中,不过这个类的命名有点蹊跷,更恰当的命名是 ThreadPoolItem,因为它确实只是一个普通的Thread。那线程调度何管理在哪里呢?也在这个类中,不过都是以静态函数方式提供的,所以这个类起到了两重作用:线程池调度和Worker线程。也由于这种原因,Resin实例中只有一个线程池,不像Tomcat和Jetty可以同时运行多个线程池,不过对于一个系统而言,一个线程池足够了。 和其他线程池实现方式不同的是,Resin采用链表保存线程。如果有请求时,就将Head移走并唤醒该线程;待运行完成后,该线程就变成空闲状态并且被添加到链表的Head部分。另外,每一个线程运行时都要判断当前空闲线程数是否超过_minSpareThreads,如果超过了,该线程就会退出(状态变成Dead),也从链表中删除。Resin如何使用该ThreadPool?所有需要用线程池的地方,只需调用ThreadPool. Schedule(Runnable)即可。该方法就是一个静态函数,顾名思义,就是将Runnable加到ThreadPool中待运行。Resin使用的还是传统方法:监听线程(com.caucho.server.port.Port),系统中可以有多个Port实例,前提端口号不同,比如有80和8080端口;另外就是Worker线程,其实就是ThreadPool中的空闲线程。Port本身是一个Thread,在启动时,会在 ThreadPool中运行5个线程——TcpConnection同时等待用户请求,当有用户请求时,其中的一个会处理。其他继续等待。当处理用户请求完成后,还可以重用这些TcpConnection,这与Jetty的有所不同,Jetty是当有用户请求时,才创建连接,处理完成后也不会重用这些连接,效率会稍差一些。另外Resin有两个后台运行线程:ThreadLauncher和ScheduleThread,前者负责当空闲线程小于最小空闲线程时创建新的线程;而后者则负责运行实际的Runnable。我觉得有的负责,没有必要用一个线程来创建新线程,多此一举。不过ScheduleThread是必须的,因为它就是Worker线程。

      介绍了tomcat、jetty和resin三种Java Web容器的线程池后,按照惯例应该比较它们的优缺点。不过先总结线程池的特点。

      线程池作为提高程序处理数据能力的一种方案,应用非常广泛。大量的服务器都或多或少的使用到了线程池技术,不管是用Java还是C++实现,线程池都有如下的特点:
线程池一般有三个重要参数:
1. 最大线程数。在程序运行的任何时候,线程数总数都不会超过这个数。如果请求数量超过最大数时,则会等待其他线程结束后再处理。
2. 最大共享线程数,即最大空闲线程数。如果当前的空闲线程数超过该值,则多余的线程会被杀掉。
3. 最小共享线程数,即最小空闲线程数。如果当前的空闲数小于该值,则一次性创建这个数量的空闲线程,所以它本身也是一个创建线程的步长。

线程池有两个概念:
1. Worker线程。工作线程主要是运行执行代码,有两种状态:空闲状态和运行状态。在空闲状态时,类似“休眠”,等待任务;处理运行状态时,表示正在运行任务(Runnable)。
2. 辅助线程。主要负责监控线程池的状态:空闲线程是否超过最大空闲线程数或者小于最小空闲线程数等。如果不满足要求,就调整之。

如果按照上述标准去考察这三个容器就会发现:Tomcat实现的线程池是最完备的,Resin次之,而Jetty最为简单。Jetty没有控制空闲线程的数量,可能最后空闲线程数会达到最大线程数,影像性能,毕竟即使是休眠线程也会耗费CPU时钟的。
谈谈Resin的线程池。Resin的实现比Tomcat复杂些。也有上述三个参数,也有两个概念,这与Tomcat相当。但考虑到如何使用ThreadPool时,Resin也要复杂些。
或许由于Resin的ThreadPool是单间模式的,所有使用ThreadPool的线程都是相同设置,比如相同的最大线程数,最大空闲线程数等,在使用它时会多些考虑。比如在控制最大Socket连接数时,com.caucho.server.port.Port还要有自己的一套控制“数量”的机制,而无法使用ThreadPool所特有的控制机制。所以使用起来比Tomcat复杂。
Tomcat使用ThreadPool却很简单。由于Tomcat的ThreadPool可以有不同的实例存在,很方便的定制属于自己的“数量”控制,直接用ThreadPool控制Socket连接数量。所以代码也比较清爽。
如果要使用线程池,那就用Tomcat的ThreadPool吧。

 

 说明:本文转自网络。

分享到:
评论

相关推荐

    Tomcat线程池实现简介

    【标题】:深入理解Tomcat线程池机制 【描述】:Tomcat作为最流行的Java Web容器,其高效处理请求的能力离不开线程池的优化。本文将详细解析Tomcat线程池的实现原理,帮助读者深入理解其工作方式。 【标签】:Tomcat ...

    TOMCAT的线程池源码

    《深入解析Tomcat线程池源码》 Tomcat作为一款广泛应用的开源Servlet容器,其在性能优化上有着独到之处。线程池是Tomcat处理并发请求的关键组件,它负责调度和管理线程,有效地提高了服务器的响应速度和并发处理...

    基于tomcat的连接数与线程池详解

    在Tomcat的配置和性能优化中,了解如何设置连接数和线程池是至关重要的。 首先,Connector按照处理连接的方式可以分为不同的协议类型,包括BIO(阻塞IO)、NIO(非阻塞IO)、APR(Apache Portable Runtime)。BIO...

    Tomcat6.0共享线程池配置

    ### Tomcat 6.0 共享线程池配置详解 #### 一、引言 在探讨Tomcat 6.0共享线程池配置之前,我们先了解下什么是线程池以及为什么要在Tomcat服务器中配置...希望本文能帮助你在实际项目中更好地利用Tomcat线程池的功能。

    线程池的一个demo

    springboot线程池使用的一个demo,注解开发,很简单,下载即用

    apache-tomcat-8.5配置优化后

    Apache Tomcat 8.5是Java Servlet容器,用于运行Java Web应用程序。它的配置优化对于提升应用性能、确保稳定性和安全至关重要。在这个配置优化后版本中,我们已经设置好了管理员的用户名和密码,允许用户直接使用...

    可用指标 提供以下与 Tomcat 相关的指标: 线程池指标 会话指标 请求处理器指标 数据库连接池指标 Tomcat 版本信

    线程池指标 会话指标 请求处理器指标 数据库连接池指标 Tomcat 版本信息 Servlet 响应时间指标 数据库响应时间指标 使用此库 如果你以常规非嵌入式方式运行 Tomcat,我们推荐将以下 JAR 文件(请参阅 pom.xml 获取...

    Tomcat深入剖析pdf+源码(Tomcat运行原理)

    9. **JMX管理**:通过Java Management Extensions(JMX),开发者可以远程监控和管理Tomcat的运行状态,如查看线程池状态、内存使用情况等。 10. **性能优化**:优化Tomcat涉及调整线程池大小、缓存设置、减少上...

    tomcat 7 和 tomcat 8

    7. 性能优化:Tomcat 8在内存管理、线程池和垃圾收集等方面进行了优化,提升了服务器的性能。 8. 安全增强:提供了更多的安全特性,如更好的密码加密存储和更强的身份验证机制。 总结来说,Tomcat 7和Tomcat 8在...

    tomcat 工作原理

    NIO模式下,Tomcat使用一个线程池处理多个连接,提高了并发性能。 五、Session管理 Tomcat提供了内置的Session管理,包括会话创建、有效期设置、会话跟踪、分布式环境下的会话复制等。默认情况下,Session信息存储...

    Tomcat使用维护手册

    本文档将详细介绍 Tomcat 的维护操作、命令、配置文件、调优参数和线程池配置等方面的知识点。 一、Tomcat 优化方法探讨 1. 修改 enableLookups 参数:将 enableLookups 设置为 false,可以避免对每个请求进行反...

    tomcat自动关闭原因

    - **线程池配置**:Tomcat的线程池配置不当,如最大线程数设置过小,当并发请求过多时,无法及时处理所有请求,最终可能导致Tomcat服务崩溃。 ### 2. 操作系统资源限制 操作系统层面的资源限制也是Tomcat自动关闭...

    Tomcat7.0 tomcat最新版本

    通过改进线程池管理和内存管理,它能更好地应对高流量网站的需求。 4. **增强的安全性**:Tomcat 7.0加强了安全特性,提供了更强的身份验证和授权机制,如集成Spring Security和JAAS,以及对SSL/TLS的改进支持,...

    How Tomcat Works 中文版+例程源码

    6. **线程池**:Tomcat使用线程池来处理并发请求,提高系统效率。当请求到来时,Tomcat会从线程池中获取一个空闲线程来执行请求,而不是每次请求都创建新线程,这样可以减少系统资源消耗。 7. **配置文件**:Tomcat...

    tomcat8.0.5手册,帮助文档

    4. 优化Tomcat线程池设置,根据实际负载调整。 七、故障排查 1. 查看`logs/catalina.out`、`logs/host-manager.*.log`和`logs/manager.*.log`等日志文件。 2. 使用JMX监控Tomcat运行状态,如内存使用、线程池情况等...

    tomcat压缩包

    7. **线程池**:Tomcat使用Executor线程池管理请求处理线程,通过调整线程池参数可以优化服务器的并发性能。 8. **日志系统**:Tomcat内置了日志系统,通过配置`logging.properties`文件,可以定制日志输出,便于...

    Tomcat 接口文档 API

    8. **JMX (Java Management Extensions)**: Tomcat使用JMX暴露管理MBeans(Managed Beans),允许通过JMX代理远程监控和管理Tomcat服务器的状态,包括启动/停止应用、查看线程池状态、调整配置等。 9. **Clustering...

    Tomcat服务器配置 视频教程 Tomcat视频

    1. **Tomcat简介**:Tomcat是一个开源的、轻量级的Java Servlet容器,用于运行Java Web应用程序,特别是基于Java EE的JSP和Servlet。它由Apache软件基金会维护,是许多Web开发者的首选服务器。 2. **安装与环境配置...

    tomcat-jdbc数据源所需jar包tomcat-jdbc.jar+tomcat-juli.jar

    此外,它实现了Java 5及更高版本的Executor接口,可以实现线程池,进一步提高性能。 2. **tomcat-juli.jar**: Juli是Tomcat的一个日志框架,全称为Apache Tomcat Utility for Logging Interface。这个jar文件包含...

Global site tag (gtag.js) - Google Analytics