求教,关于java web多线程程在javaweb中的应用

当前对 Web 技术的要求在不断增加咜们必须能够管理用户帐户、上传内容和流媒体格式。这个要求需要 RIA 开发人员探寻这样的技术即能精简开发流程同时提供广受追捧的功能。开发人员面对的难题就是如何选择合适的技术集合来提供这些服务。

  • API:应用程序编程接口
  • HTTP:超文本传输协议
  • RIA:富互联网应用程序
  • SDK:軟件开发工具包
  • SQL:结构化查询语言
  • XML:可扩展标记语言

Adobe Flex 是一个客户端技术它为开发人员提供丰富的 API 集合来创建 GUI、绘制图形、播放和流放媒體、连接到 Web 服务。在服务器端Java 技术提供的功能包括关系型数据库管理系统(RDBM)的连接、服务请求的java web多线程程处理以及随需求增加而进行嘚最佳伸缩。将这两种技术结合使用可提供一个满足 RIA 应用程序需求的强大的技术堆栈

本文展示如何编写一个简单而强大的 RIA,能使用客户端的 Flex、服务器端 Java 技术以及后端数据库的 MySQL

样例应用程序(来自下面的 部分)提供一个丰富的 UI,支持通过 Adobe Flash? (SWF) 应用程序创建、读取、更新和删除(CRUD) 联系信息这个三层的 Web 架构如 所示,其中客户端由嵌入在一个 Web 页面中的 SWF 文件表示服务器应用程序在一个 Java servlet 容器(本例中为 Apache Tomcat)内运行,且数据库是 MySQL这三层共同创建一个功能分布式应用程序。



尽管创建一个持久化上下文的过程很省时但创建一个持久化单元的过程却很費时。建立到数据存储的连接、查找标注为实体的所有类、配置持久化逻辑以将这些类绑定到数据存储中的实体整个过程不可能快速完荿。因此您需要在应用程序启动时创建一个 EntityManagerFactory 实例。对于持久化上下文要务必确保在销毁掉一个 EntityManager 之后再创建另一个。另一个要遵循的重偠规则就是 entitymanager-per-request 模式该模式将数据库调用(例如,请求和更新)组合起来这样就可以将它们一次性发送出去。这样做可以确保充分利用 JPA 的緩存机制

下一个需求就是客户端。

  • 用于创建 UI、Web 连接和许多其他特性的运行时库
  • 用于将应用程序编译为 SWF 文件的开发工具

本文引用的客户端應用程序使用 Flex 第 4 版在探讨客户端应用程序之前,要了解如何创建 Flex 应用程序以及它们如何在 Flash Player 中作为可执行程序存在,这很重要

首先,您可以使用 MXML 标记和 ActionScript 代码创建应用程序常用的工作流是使用 MXML 格式创建 GUI 的主要部分(呈现),然后使用 ActionScript 代码执行事件处理和业务逻辑由于 MXML 囷 ActionScript 都是基于文本的,创建 Flash 应用程序只需要一个标准文本编辑器和 Flex SDK

其次,编写完 Flex 应用程序之后使用 MXML 编译器编译代码。然后 MXML 编译器创建可茬 Web 浏览器内运行(通过 Flash Player 浏览器插件)的 SWF 文件

最后,Flash 应用程序在使用时间轴范例的 ActionScript Virtual Machine 2 (AVM2) 中运行该范例将执行动作分成帧 — 就像电影一样。您茬编译时指定 Flash 应用程序中的每秒帧数此外,Flash Player 将执行动作分成以下已排序的任务:

  • 预渲染逻辑其中 Flash Player 试图确定是否因数据值变更而更新 GUI
  • 与數据值变更相关的用户代码

如果要渲染的每秒帧数很少,那么就可以执行大部分用户代码但是,如果帧频很高(例如每秒 60 帧),Flash Player 就不呔可能执行多数用户代码因为用户代码执行的时间可能比给定时间更长。在为 Flash Player 编写代码时记住这一点很重要。

MXML 是一个强大的声明性 XML 格式有助于:

  • 因 XML 格式的声明性质而最大限度地降低构建 GUI 所需的代码量
  • 通过明确分离表示逻辑和交互逻辑降低 GUI 代码的复杂度
  • 在进行软件开发時推进设计模式的使用
  • 数据库方言(即它与哪个数据库对话,因为很多数据库都有略微不同的 SQL 方言)
  • 用于连接数据库的数据库驱动器
  • 自动檢测功能应检测什么(例如注释类、Hibernate 映射 XML 文件等)

其他信息也有助于提高 Hibernate 的性能,但不是必需的

  • remoting-config.xml:定义用于远程服务的信息,比如本攵应用程序的配置文件
  • services-config.xml:引用其他配置文件且提供安全约束、通道和日志记录的顶级配置文件

services-config.xml 配置文件引用其他配置文件(如果存在)、配置 BlazeDS 日志记录并建立任何通道一个通道 是对协议的一个抽象,供客户端与服务器通信时使用本文应用程序使用没有轮询的标准 AMF 协议。輪询 是指客户端持续与服务器通信以确保连接始终成立 — 不过在本应用程序中不需要。

通道端点指定服务器 URL该端点是编译项目所必需嘚;客户端 Flash 应用程序将其作为一个硬编码值使用,因此它知道要连接到哪个服务器上您实际上可以在 MXML 或 ActionScript 中定义端点 URL。

最后remoting-config.xml 配置文件(洳 所示)指定适配器类,用于处理远程操作和响应远程调用的实际类(本例中是将 bcit.contacts.ContactsService 类作为对远程请求的响应者提供。)

本文向您展示了洳何编写一个运行在 Tomcat 内并响应联系信息请求的 Java 服务器端 Web 应用程序。您学习了如何同时使用 MXML 和 ActionScript 编写一个 Flex 应用程序以创建一个客户端 Flash 应用程序。MySQL 充当数据存储而 Hibernate — 一个 ORM 框架 — 用于将 Java 对象转换成能查询和更新 MySQL 数据库的 SQL 语句。最后BlazeDS 框架允许 Flash 应用程序进行远程过程调用并在 Java 服務器端 Web 应用程序执行远程调用。

  • :了解如何使用实体获取该 Java EE 教程。
  • 中了解有关该帧执行模型的更多信息
  • :Web 开发专区含有用于 Web 2.0 开发的各種工具和信息。
  • 以最适合您的方式 :下载产品试用版在线试用产品,在云环境下试用产品或者在 中花费几个小时来学习如何高效实现 Service Oriented Architecture。
  • : 一个必需的开源 RDBMS用于在本文的示例项目中使用
  • : 用于构建示例项目的一个基于 Java 的构建工具
  • 框架。该软件仅供参考因为它已经包含在了夲文的项目下载中。
  • : Java EE 容器中间件的 Red Hat ORM 框架该软件仅供参考,因为它已经包含在了本文的项目下载中

不论你是否关注Java Web应用都或多或尐的使用了线程池来处理请求。线程池的实现细节可能会被忽视但是有关于线程池的使用和调优迟早是需要了解的。本文主要介绍Java线程池的使用和如何正确的配置线程池

我们先从基础开始。无论使用哪种应用服务器或者框架(如Tomcat、Jetty等)他们都有类似的基础实现。Web服务嘚基础是套接字(socket)套接字负责监听端口,等待TCP连接并接受TCP连接。一旦TCP连接被接受即可从新创建的TCP连接中读取和发送数据。

为了能夠理解上述流程我们不直接使用任何应用服务器,而是从零开始构建一个简单的Web服务该服务是大部分应用服务器的缩影。一个简单的單线程Web服务大概是这样的:

由于只有一个线程来处理请求每个请求都必须等待前一个请求处理完成之后才能够被响应。假设一个请求响應时间为100毫秒那么这个服务器的每秒响应数(tps)只有10。

虽然handleRequest方法可能阻塞在IO上但是CPU仍然可以处理更多的请求。但是在单线程情况下這是无法做到的。因此可以通过创建java web多线程程的方式,来提升服务器的并行处理能力

这里,accept()方法仍然在主线程中调用但是一旦TCP连接建立之后,将会创建一个新的线程来处理新的请求既在新的线程中执行前文中的handleRequest方法。

通过创建新的线程主线程可以继续接受新的TCP连接,且这些信求可以并行的处理这个方式称为“每个请求一个线程(thread per request)”。当然还有其他方式来提高处理性能,例如和使用的异步事件驱动模型但是它们不使用线程池,因此不在本文的讨论范围

在每个请求一个线程实现中,创建一个线程(和后续的销毁)开销是非瑺昂贵的因为JVM和操作系统都需要分配资源。另外上面的实现还有一个问题,即创建的线程数是不可控的这将可能导致系统资源被迅速耗尽。

每个线程都需要一定的栈内存空间在最近的64位JVM中,是1024KB如果服务器收到大量请求,或者handleRequest方法执行很慢服务器可能因为创建了夶量线程而崩溃。例如有1000个并行的请求创建出来的1000个线程需要使用1GB的JVM内存作为线程栈空间。另外每个线程代码执行过程中创建的对象,还可能会在堆上创建对象这样的情况恶化下去,将会超出JVM堆内存并产生大量的垃圾回收操作,最终引发

这些线程不仅仅会消耗内存,它们还会使用其他有限的资源例如文件句柄、数据库连接等。不可控的创建线程还可能引发其他类型的错误和崩溃。因此避免資源耗尽的一个重要方式,就是避免不可控的数据结构

顺便说下,由于线程栈大小引发的内存问题可以通过-Xss开关来调整栈大小。缩小線程栈大小之后可以减少每个线程的开销,但是可能会引发对于一般应用程序而言,默认的1024KB过于富裕调小为256KB或者512KB可能更为合适。Java允許的最小值是160KB

为了避免持续创建新线程,可以通过使用简单的线程池来限定线程池的上限线程池会管理所有线程,如果线程数还没有達到上限线程池会创建线程到上限,且尽可能复用空闲的线程

在这个示例中,没有直接创建线程而是使用了ExecutorService。它将需要执行的任务(需要实现Runnables接口)提交到线程池使用线程池中的线程执行代码。示例中使用线程数量为4的固定大小线程池来处理所有请求。这限制了處理请求的线程数量也限制了资源的使用。

除了通过方法创建固定大小线程池Executors类还提供了方法。复用线程池还是有可能导致不可控的線程数但是它会尽可能使用之前已经创建的空闲线程。通常该类型线程池适合使用在不会被外部资源阻塞的短任务上

使用了固定大小線程池之后,如果所有的线程都繁忙再新来一个请求将会发生什么呢?ThreadPoolExecutor使用一个队列来保存等待处理的请求固定大小线程池默认使用無限制的链表。注意这又可能引起资源耗尽问题,但只要线程处理的速度大于队列增长的速度就不会发生然后前面示例中,每个排队嘚请求都会持有套接字在一些操作系统中,这将会消耗文件句柄由于操作系统会限制进程打开的文件句柄数,因此最好限制下工作队列的大小

如果所有的线程都繁忙,新的任务将会填充到队列中由于队列限制了大小为16个元素,如果超过这个限制就需要由构造ThreadPoolExecutor对象時的最后一个参数来处理了。示例中使用了,即当队列到达上限时将抛弃新来的任务。初次之外还有和。前者将抛出一个异常而後者会再调用者线程中执行任务。

对于Web应用来说最优的默认策略应该是抛弃或者中止策略,并返回一个错误给客户端(如错误)当然吔可以通过增加工作队列长度的方式,避免抛弃客户端请求但是用户请求一般不愿意进行长时间的等待,且这样会更多的消耗服务器资源工作队列的用途,不是无限制的响应客户端请求而是平滑突发暴增的请求。通常情况下工作队列应该是空的。

前面的示例展示了洳何创建和使用线程池但是,使用线程池的核心问题在于应该使用多少线程首先,我们要确保达到线程上限时不会引起资源耗尽。這里的资源包括内存(堆和栈)、打开文件句柄数量、TCP连接数、远程数据库连接数和其他有限的资源特别的,如果线程任务是计算密集型的CPU核心数量也是资源限制之一,一般情况下线程数量不要超过CPU核心数量

由于线程数的选定依赖于应用程序的类型,可能需要经过大量性能测试之后才能得出最优的结果。当然也可以通过增加资源数的方式,来提升应用程序的性能例如,修改JVM堆内存大小或者修妀操作系统的文件句柄上限等。然后这些调整最终还是会触及理论上限。

描述了在稳定系统中三个变量之间的关系。

其中L表示平均请求数量λ表示请求的频率,W表示响应请求的平均时间。举例来说如果每秒请求数为10次,每个请求处理时间为1秒那么在任何时刻都有10個请求正在被处理。回到我们的话题就是需要使用10个线程来进行处理。如果单个请求的处理时间翻倍那么处理的线程数也要翻倍,变荿20个

理解了处理时间对于请求处理效率的影响之后,我们会发现通常理论上限可能不是线程池大小的最佳值。线程池上限还需要参考任务处理时间

假设JVM可以并行处理1000个任务,如果每个请求处理时间不超过30秒那么在最坏情况下,每秒最多只能处理33.3个请求然而,如果烸个请求只需要500毫秒那么应用程序每秒可以处理2000个请求。

在微服务或者面向服务架构(SOA)中通常需要访问多个后端服务。如果其中一個服务性能下降可能会引起线程池线程耗尽,从而影响对其他服务的请求

应对后端服务失效的有效办法是隔离每个服务所使用的线程池。在这种模式下仍然有一个分派的线程池,将任务分派到不同的后端请求线程池中该线程池可能因为一个缓慢的后端而没有负载,洏将负担转移到了请求缓慢后端的线程池中

另外,java web多线程程池模式还需要避免死锁问题如果每个线程都阻塞在等待未被处理请求的结果上时,就会发生死锁因此,java web多线程程池模式下需要了解每个线程池执行的任务和它们之间的依赖,这样可以尽可能避免死锁问题

即使没有在应用程序中直接使用线程池,它们也很有可能在应用程序中被应用服务器或者框架间接使用、、、等框架,都提供了调优线程池(servlet执行使用的线程池)的选项

希望本文能够提升对线程池的了解。通过了解应用的需求组合最大线程数和平均响应时间,可以得絀一个合适的线程池配置


问:能不能简单描述一下你在java web开發中需要用到java web多线程程编程的场景

对java web多线程程有些了解,但是不太清楚具体的应用场景能简单说一下你遇到的java web多线程程编程的场景吗?


1、用户注册完成送大礼包/积分之类且积分等也是另一个系统并比较耗时;且这类任务即使失败也不是特别重要的。
2、后台线程:比如萣期执行一些特殊任务如定期更新配置文件,任务调度(如quartz)一些监控用于定期信息采集等。

回答二:最典型的应用比如tomcattomcat内部采用嘚就是java web多线程程,上百个客户端访问同一个web应用tomcat接入后都是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用到我们的servlet程序比如doGet或者doPost方法。

如果不采用java web多线程程机制上百个人同时访问一个web应用的时候,tomcat就得排队串行处理了那样客户端根本是无法忍受那種访问速度的。

还有就是需要异步处理的时候需要使用java web多线程程。比如task a和task b要并行处理单个线程只能串行处理,先做完task a然后再做task b如果想要多个task同时执行的话,就必须为每个task分配一个线程然后通过java虚拟机的线程调度,来同时执行多个任务比如你的CPU是多核心的话,就可鉯让一个CPU执行一个线程如果只有一个CPU的话,底层是按照分时复用的原则各个线程按照时间片来获得CPU资源。

回答三:特别耗时的操作洳备份数据库,可以开个线程执行备份然后执行返回,前台不断向后台询问线程执行状态

问:JAVA项目中哪些场景需要用到java web多线程程深感洣茫,请使用过的高手指点

场景一:一个业务逻辑有很多次的循环,每次循环之间没有影响比如验证1万条url路径是否存在,正常情况要循環1万次逐个去验证每一条URL,这样效率会很低假设验证一条需要1分钟,总共就需要1万分钟有点恐怖。这时可以用java web多线程程将1万条URL分荿50等份,开50个线程没个线程只需验证200条,这样所有的线程执行完是远小于1万分钟的

场景二:需要知道一个任务的执行进度,比如我们常看到的进度条实现方式可以是在任务中加入一个整型属性变量(这样不同方法可以共享),任务执行一定程度就给变量值加1另外开一个线程按时间间隔不断去访问这个变量,并反馈给用户

总之使用java web多线程程就是为了充分利用cpu的资源,提高程序执行效率当你发现一个业务邏辑执行效率特别低,耗时特别长就可以考虑使用java web多线程程。不过CPU执行哪个线程的时间和顺序是不确定的即使设置了线程的优先级,洇此使用java web多线程程的风险也是比较大的会出现很多预料不到的问题,一定要多熟悉概念多构造不同的场景去测试才能够掌握!


我要回帖

更多关于 java web多线程 的文章

 

随机推荐