`
wanxiaotao12
  • 浏览: 455849 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【转】action,servlet单实例线程安全问题

阅读更多

 

一,servlet容器如何同时处理多个请求。

Servlet采用多线程来处理多个请求同时访问,Servelet容器维护了一个线程池来服务请求。
线程池实际上是等待执行代码的一组线程叫做工作者线程(Worker Thread),Servlet容器使用一个调度线程来管理工作者线程(Dispatcher Thread)。

当容器收到一个访问Servlet的请求,调度者线程从线程池中选出一个工作者线程,将请求传递给该线程,然后由该线程来执行Servlet的service方法。
当这个线程正在执行的时候,容器收到另外一个请求,调度者线程将从池中选出另外一个工作者线程来服务新的请求,容器并不关系这个请求是否访问的是同一个Servlet还是另外一个Servlet。
当容器同时收到对同一Servlet的多个请求,那这个Servlet的service方法将在多线程中并发的执行。


二,Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间。对于Tomcat可以在server.xml中通过<Connector>元素设置线程池中线程的数目。

就实现来说:
  调度者线程类所担负的责任如其名字,该类的责任是调度线程,只需要利用自己的属性完成自己的责任。所以该类是承担了责任的,并且该类的责任又集中到唯一的单体对象中。
而其他对象又依赖于该特定对象所承担的责任,我们就需要得到该特定对象。那该类就是一个单例模式的实现了。

三,如何开发线程安全的Servlet                                                                                                                 
 1,变量的线程安全:这里的变量指字段和共享数据(如表单参数值)。

  a,将 参数变量 本地化。多线程并不共享局部变量.所以我们要尽可能的在servlet中使用局部变量。
   例如:String user = "";
         user = request.getParameter("user");

  b,使用同步块Synchronized,防止可能异步调用的代码块。这意味着线程需要排队处理。
  在使用同板块的时候要尽可能的缩小同步代码的范围,不要直接在sevice方法和响应方法上使用同步,这样会严重影响性能。

 

 2,属性的线程安全:ServletContext,HttpSession,ServletRequest对象中属性
  ServletContext:(线程是不安全的)
   ServletContext是可以多线程同时读/写属性的,线程是不安全的。要对属性的读写进行同步处理或者进行深度Clone()。
   所以在Servlet上下文中尽可能少量保存会被修改(写)的数据,可以采取其他方式在多个Servlet中共享,比方我们可以使用单例模式来处理共享数据。
  HttpSession:(线程是不安全的)
   HttpSession对象在用户会话期间存在,只能在处理属于同一个Session的请求的线程中被访问,因此Session对象的属性访问理论上是线程安全的。
   当用户打开多个同属于一个进程的浏览器窗口,在这些窗口的访问属于同一个Session,会出现多次请求,需要多个工作线程来处理请求,可能造成同时多线程读写属性。
   这时我们需要对属性的读写进行同步处理:使用同步块Synchronized和使用读/写器来解决。

  ServletRequest:(线程是安全的)
   对于每一个请求,由一个工作线程来执行,都会创建有一个新的ServletRequest对象,所以ServletRequest对象只能在一个线程中被访问。ServletRequest是线程安全的。
   注意:ServletRequest对象在service方法的范围内是有效的,不要试图在service方法结束后仍然保存请求对象的引用。

 3,使用同步的集合类:
  使用Vector代替ArrayList,使用Hashtable代替HashMap。

 4,不要在Servlet中创建自己的线程来完成某个功能。
  Servlet本身就是多线程的,在Servlet中再创建线程,将导致执行情况复杂化,出现多线程安全问题。

 5,在多个servlet中对外部对象(比方文件)进行修改操作一定要加锁,做到互斥的访问。 

四,SingleThreadModel接口
 javax.servlet.SingleThreadModel接口是一个标识接口,如果一个Servlet实现了这个接口,那Servlet容器将保证在一个时刻仅有一个线程可以在给定的servlet实例的service方法中执行。将其他所有请求进行排队。
 服务器可以使用多个实例来处理请求,代替单个实例的请求排队带来的效益问题。服务器创建一个Servlet类的多个Servlet实例组成的实例池,对于每个请求分配Servlet实例进行响应处理,之后放回到实例池中等待下此请求。这样就造成并发访问的问题。
 此时,局部变量(字段)也是安全的,但对于全局变量和共享数据是不安全的,需要进行同步处理。而对于这样多实例的情况SingleThreadModel接口并不能解决并发访问问题。
 
 SingleThreadModel接口在servlet规范中已经被废弃了。

 

 

Servlet的生命周期:

1.       Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法;

2.       service运行请求对应的doXXX(doGet,doPost)方法;

3.       服务器销毁实例,运行其destory方法;

Servlet的生命周期由Servlet容器管理;

(三个概念的理解:

Servlet容器 < Web容器 < 应用服务器?

Servlet容器的主要任务就是管理Servlet的生命周期;

Web容器也称之为web服务器,主要任务就是管理和部署web应用的;

应用服务器的功能非常强大,不仅可以管理和部署web应用,也可以部署EJB应用,实现容器管理的事务等等。。。

 

Web服务器就是跟基于HTTP的请求打交道,而EJB容器更多是跟数据库,事务管理等服务接口交互,所以应用服务器的功能是很多的。

常见的web服务器就是Tomcat,Tomcat同样也是Servlet服务器;

常见的应用服务器有WebLogicWebSphere,但都是收费的;

没有Servlet容器,可以用Web容器直接访问静态Html页面,比如安装了apache等;如果需要显示Jsp/Servlet,就需要安装一个Servlet容器;但是光有servlet容器也是不够的,它需要被解析为html显示,所以仍需要一个web容器;所以,我们常把web容器和Servlet容器视为一体,因为他们两个容器都有对方的功能实现了,都没有独立的存在了,比如tomcat

 

Servlet是如何处理多个请求同时访问呢?

Servlet容器默认是采用单实例多线程的方式处理多个请求的:

1.       web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例);

2.       容器初始化Servlet。主要就是读取配置文件(例如tomcat,可以通过servlet.xml<Connector>设置线程池中线程数目,初始化线程池;通过web.xml,初始化每个参数值等等);

3.       当请求到达时,Servlet容器通过调度线程(Dispatchaer Thread)调度它管理下的线程池中等待执行的线程(Worker Thread)给请求者;

4.       线程执行Servletservice方法;

5.       请求结束,放回线程池,等到被调用;

从上面可以看出:

第一:Servlet单实例,减少了产生servlet的开销;

第二:通过线程池来响应多个请求,提高了请求的响应时间;

第三:Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet,直接分配给它一个新的线程;如果是同一个Servlet的多个请求,那么Servletservice方法将在多线程中并发的执行;

第四:每一个请求由ServletRequest对象来接受请求,由ServletResponse对象来响应该请求;

 

问题出现了:

同一个Servlet的的多个请求到来时,可能发生多线程同时访问同一资源的情况,数据可能变的不一致,可能会因为线程安全问题发生错误。

解决:

1.       实现 SingleThreadModel 接口

如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题;

2.       同步对共享数据的操作

使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,Servlet可以通过同步块操作来保证线程的安全。

ServletRequest对象是线程安全的,但是ServletContextHttpSession不是线程安全的;

要使用同步的集合类: Vector代替ArrayListHsahTable代替HashMap

3.       避免使用实例变量(成员变量)

线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。

 

对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。SingleThreadModelServlet2.4中 已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行 它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。

  Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。

 

 

 

在使用Struts1.XStruts2.X的时候我们会发现一个很明显的问题:

Action的单实例问题:Struts1.X中的Action都是Singleton的,但是Struts2.X中,Action却全是Prototype的;尤其是我们配置SSH三层架构的时候,让Spring来管理Struts的Action,如果使用Struts2,默认都要为每个Action的Bean添加 prototype="true" 这个属性,不然,当操作一个对象时候,无论你是删除还是修改一个对象,始终会出现最后一次访问的记录;

为什么Struts2会采用Prototype模式而不是单实例的模式呢?

首先,单实例的模式最主要的一个问题就是线程同步问题,如果处理不好程序会有严重的bug,Servlet就是一个例子,它同样是采用单实例多线程的模式,如果多线程中同时访问一个共享的数据,没有加同步锁(synchronized),可能会出现数据错误。

这就找到了单实例引线程安全问题的原因:单实例是否能够被状态化,通俗一点说,就是单实例类中是否可以有成员变量,有成员变量的就是状态,无成员变量就是无状态;

状态化的单实例适用的场合是作为一个状态窗户对外提供服务;比如,论坛帖子的计数器,用户浏览一次,就synchronized的加1;

无状态的单实例适用的场合就是工具性质的功能,常用的Servlet就属于此类,每次来个请求,都始终是一个Servlet实例在工作;(所有Servlet是不建议在它里面定义成员变量的,就是有也必须synchronized处理);

我们知道:

Struts1.X中Action从页面获得数据是从FormBean获得的,它不需要定义成员变量获取页面数据,所有它是无状态的,采用的是单实例模式,而且线程安全的;

Struts2.X 采用值绑定模式,在Action页面定义了成员变量与页面的标签name绑定,一次获取页面数据,它是有状态的,很容易出现线程安全问题,如果单纯用 synchronized处理,并发访问肯定出问题,所有它采用了prototype模式,就是非单实例的模式,线程不安全;

有人担心Struts2每次都创建Action会有性能问题。

分享到:
评论

相关推荐

    servlet与Struts action线程安全问题分析(pdf)

    servlet与Struts action线程安全问题分析 &lt;br&gt;===================================================== Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    第11章 开发线程安全的servlet 350 11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    第11章 开发线程安全的servlet 350 11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    第11章 开发线程安全的servlet 350 11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    第11章 开发线程安全的servlet 350 11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 ...

    Struts1与Struts2本质区别

    Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。 3 Servlet依赖方面的对比:Struts 1 Action依赖于Servlet API,因为Struts 1 Action的execute方法中有HttpServletRequest和...

    JAVA-SSH面试题

    struts2.0 Action为每一个请求产生一个实例,因此没有线程安全问题。 c、Servlet依赖 struts1.2 Action依赖于Servlet API,因为当一个Action被调用时HttpServletRequest和HttpServletResponse被传递给execut方法。 ...

    超级有影响力霸气的Java面试题大全文档

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    java面试题

    答:Struts1和Struts2是两个完全不同的框架,Struts1以ActionServlet作为核心控制器,由ActionServlet负责拦截用户的所有请求。Struts2以核心控制器FilterDispatcher为基础,包含了框架内部的控制流程和处理机制。 ...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    Java语言基础下载

    Struts ActionServlet控制器对象 642 Struts Action Classes 642 搞定Action对象 643 处理异常 643 Action的分类 643 Struts Action Mapping 646 使用ActionForward导航 647 Struts ActionForm Bean捕获表单数据 648...

    从J2SE到J2EE知识点介绍

    9.实例成员和类成员 13 (四).类的继承 14 1.创建子类 14 2.this引用、super引用 16 3.最终类和抽象类 18 (五).类的多态性 18 1. 方法的重载 18 2. 方法的覆盖 18 (六).接口 20 1. 定义接口 20 2. 实现接口 21 ...

    Java编程艺术 PDF

    mail客户端和Internet搜索、用Java修饰HTML、显示统计图表、金融应用中的Applet和Servlet、基于AI的问题求解等,每章给出的示例代码都可以直接运行,无需修改,而且读者可以从www.osborne.com站点上免费下载这些代码...

    java 编程艺术

    mail客户端和Internet搜索、用Java修饰HTML、显示统计图表、金融应用中的Applet和Servlet、基于AI的问题求解等,每章给出的示例代码都可以直接运行,无需修改,而且读者可以从www.osborne.com站点上免费下载这些代码...

    疯狂Android讲义源码

     4.3.2 Activity与Servlet的相似性与  区别 194  4.4 本章小结 195  第5章 使用Intent和IntentFilter  第5章 进行通信 196  5.1 Intent对象详解 197  5.1.1 使用Intent启动系统组件 197  5.2 Intent的属性及...

    Google Android SDK开发范例大全(完整版)

    余志龙,来自于手机制造业、电视媒体业、网络、电信产业、软件开发等领域,擅长嵌入式系统软件设计、J2ME游戏开发、Android开发,以及J2EE、JSP、Servlet、JavaBeans、PHP、C#等程序语言,熟悉面向对象技术与Eclipse...

    Java面试宝典2010版

    5、谈谈Struts中的Action servlet。 6、Struts优缺点 优点: 1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现. 7、STRUTS的应用(如STRUTS架构) 8、说说struts1与struts2的区别。 9、hibernate中的update...

    最新Java面试宝典pdf版

    5、谈谈Struts中的Action servlet。 120 6、Struts优缺点 优点: 1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现. 120 7、STRUTS的应用(如STRUTS架构) 121 8、说说struts1与struts2的区别。 121 9、...

    Java面试笔试资料大全

    5、谈谈Struts中的Action servlet。 120 6、Struts优缺点 优点: 1. 实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现. 120 7、STRUTS的应用(如STRUTS架构) 121 8、说说struts1与struts2的区别。 121 9、...

Global site tag (gtag.js) - Google Analytics