Web容器线程池机制小议

标签: web 容器 线程池 | 发表时间:2013-02-16 11:17 | 作者:
出处:http://www.iteye.com
基础
从刚开始学习java,我们就被告知Java是一种支持多线程的语言,每条程序指令都会在一个线程中执行,而启动主线程的入口,是可执行类中的main方法。我们可以在main方法或其调用的方法中创建新的线程以实现多线程、并发处理的效果。
Java入门资料上介绍线程时往往会说明一点,创建线程不是免费的,是有成本的--对内存的消耗、对CPU切换调度的消耗都是成本,所以像数据库连接池这类“创建昂贵型”资源一样,创建好的线程优先被复用而不是每次都创建新的,这就是线程池出现的原因。
用户请求进入JVM的途径
我们可以把启动后的JVM看成一个一间屋子,用户请求要进入这个屋子,上面提到的main方法是一种 - 启动的时候即传入请求参数。另外一种最常用的方式,就是Socket。Java IO体系中的socket类为这个屋子打开了一扇门,使得各种在socket之上的协议请求可以自由地进入到JVM中,其中最大的一种应用,是servlet编程模型。各类基于request-reply的同步请求,不论是基本的socket请求,还是http、web service、hessian请求,都是通过最基本的socket抽象,进入到JVM的屋子并进而被JVM内的线程处理。 - 这是Web容器实现的基本原理。
我们常用的另一种请求进入JVM的途径是通过JMS消息,追到底层实现仍然逃不出socket抽象,只不过这种方式可以实现除request-reply外更多的消息交换模式(MEP)而已。
Web容器机理
有了上面的铺垫,我们不难想象web容器(tomcat,jetty,weblogic等)的实现了:通过一个主入口类提供的main方法,创建一个服务端socket管理类监听指定的socket,创建一个线程池处理接收到的请求。典型场景是:来自客户端的socket请求到达服务端时,服务端socket管理类提取这个socket实例,然后将它交给线程池中一个空闲线程处理,处理完毕后,线程回到连接池,结果写回客户端,socket被关闭。
常见的web容器(tomcat,jetty,weblogic等)都是基于这种socket+连接池的模型来实现的,以tomcat为例,其server.xml文件中配置的Connector标签是对不同socket之上的协议的抽象,如处理http的connector,处理https的connector,处理ajp的connector等。Executor标签是对连接池的抽象,可以配置最大线程数,初始线程数等。可以用如下方式查看windows下运行态的tomcat线程:
在tomcat运行窗口中,按下快捷键ctrl + break。之后JVM内的线程信息就会输出到tomcat运行窗口中,在其中我们可以看到类似“"TP-Processor4" daemon prio=6 tid=0x0325ae30 nid=0x10a0 runnable”这样的线程,就是线程池中等待处理用户请求的线程,还有类似“"http-8080-Acceptor-0" daemon prio=6 tid=0x0327b588 nid=0x6a0 runnable”这样的线程,可以推断是接收客户端socket请求的线程。
拓展话题
到这里,我们讲清楚了web容器工作的基本原理,这个可以解释为什么tomcat出现threadpool full的错误---线程池中所有的线程都忙着呢,当然它们都在忙啥,还需要进一步挖掘。
值得注意的问题一:Quartz框架内置了自己的线程池,应该不是使用服务器的线程工作的(这个我还没有通过试验证明,依稀记得配置了Quartz的应用启动后tomcat的thread dump会不同)。
值得注意的问题二:因为我们的应用程序是在web容器的线程上下文中执行,所以一些线程相关的操作会直接影响到服务器的线程池(例如建超的测试中就是把所有web容器线程池中的线程给催眠了),一个容易犯编程错误的场景是ThreadLocal的使用。ThreadLocal的生命周期是在一个线程内,在线程池内的线程生命周期被延长了,一个线程在其生命周期内可能处理无数个用户请求,所以不要假设你放在ThreadLocal内的变量只会被单个请求处理中被访问。
值得注意的问题三:上面提到的一个socket请求一个线程来处理的方式是传统的web容器实现方式,Java NIO带来了异步IO处理之后,Web容器以及JEE规范也在发生着变化。Web容器的变化是对NIO的支持,使用NIO,可以让少数的线程处理更多的来自客户的并发请求而不再像上面那样线程池耗尽后无法处理用户请求,以tomcat为例,6.x版本之后提供了对NIO的支持,可以通过配置Http11NioProtocol取代原来的Http11Protocol来实现。JEE规范的变化是Servlet 3中对异步请求处理的支持,可以实现服务器推等目前编程模型中无法想象的效果。

关于线程安全
1、线程安全的第一重意义,是指Java中的某些基本类型的操作是非原子性的,比如long和double这类64位变量,当线程把主存中的long或者double型的数据读入线程内存时,可能是两次读操作,写入也是,这种非原子性的类型操作在多线程环境下会出现问题--一个简单的double d = 1.0d指令的执行都可能会出错!
2、线程安全的第二重意义,是我们常说的这种多线程对宏观业务逻辑的影响,多个线程中的代码读写主存中的变量值,使得单个线程中的处理逻辑发生紊乱乃至错误。
ThreadLocal是解决多线程问题的一种,其他的还有synchronized关键字、volatile关键字等。
虽然说开发服务端程序不用太多的考虑多线程问题,但是不代表不需要考虑,下面提到的点就是web应用开发中需要注意多线程问题的地方:
1、单例。Spring管理的controller、service,structs 1中的action,这些对象都是单例的,可写的实例变量存在多线程问题。
2、共享资源。比如数据库连接,文件句柄等,这些显然不能被多线程共享使用。
3、框架的线程安全问题。比如hibernate文档就声明了其session类不是线程安全的。
4、Java基础框架中的线程问题。比如Servlet规范明确说明了对HttpSession类中attribute的处理是需要开发者来保证线程安全的;早期的EJB规范中也明确禁止在Bean中进行多线程编程。

已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [web 容器 线程池] 推荐:

Web容器线程池机制小议

- - ITeye博客
从刚开始学习java,我们就被告知Java是一种支持多线程的语言,每条程序指令都会在一个线程中执行,而启动主线程的入口,是可执行类中的main方法. 我们可以在main方法或其调用的方法中创建新的线程以实现多线程、并发处理的效果. Java入门资料上介绍线程时往往会说明一点,创建线程不是免费的,是有成本的--对内存的消耗、对CPU切换调度的消耗都是成本,所以像数据库连接池这类“创建昂贵型”资源一样,创建好的线程优先被复用而不是每次都创建新的,这就是线程池出现的原因.

Java线程池

- - 企业架构 - ITeye博客
线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的. 在jdk1.5之后这一情况有了很大的改观. Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用. 为我们在开发中处理线程的问题提供了非常大的帮助.

Java 线程池

- - 编程语言 - ITeye博客
在项目中,系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互. 在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存周期很短的线程时,更应该考虑使用线程池. 使用线程池可以有效地控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能剧烈下降,甚至导致JVM崩溃,而线程池的最大线程数参数可以控制系统中并发线程数不超过此数.

java线程池分析

- - BlogJava-首页技术区
    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成. 5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务.

Java线程池应用

- - CSDN博客架构设计推荐文章
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务. 2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机). Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.

Java线程池总结

- - Java - 编程语言 - ITeye博客
  假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间. 当T1 + T3 远大于 T2时,采用多线程技术可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.     线程池就是一个线程的容器,每次只执行额定数量的线程, 线程池作用就是限制系统中执行线程的数量.

java 线程池原理及几种线程池详解

- - CSDN博客综合推荐文章
服务器经常出现处理大量单个任务处理的时间很短而请求的数目却是巨大的请求. 构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务. 实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显.

Spring提供的线程池支持

- - 博客园_首页
核心提示:一旦企业应用越来越复杂时(比如,基于流程服务器的EIS),它们对相关技术也提出了更高的要求. 在使用 EJB 3.0组件技术开发企业应用过程中,它们能够享受到EJB容器提供的线程池、任务调度(@Timeout)服务. 现如今,运行于Web容器的Web应用、单独的桌面应用. 一旦企业应用越来越复杂时(比如,基于流程服务器的EIS),它们对相关技术也提出了更高的要求.

用线程池启动定时器

- - BlogJava-首页技术区
(1)调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务. (2)支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式.             System.out.println("响");           .

Java四种线程池的使用

- - ITeye博客
Java通过Executors提供四种线程池,分别为:. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行.