`
sooxin
  • 浏览: 255755 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

深入研究Servlet线程安全性问题

    博客分类:
  • J2EE
 
阅读更多

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。

  狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

  最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

  深入研究Servlet线程安全性问题

  在探讨java线程安全前,让我们先简要介绍一下Java语言。

  任何语言,如C++,C#,Java,它们都有相通之处,特别是语法,但如果有人问你,Java语言的核心是什么?类库?关键字?语法?似乎都不 是。Java语言的核心,也就是Sun始终不愿意开源的东西:Java虚拟机的实现(不过sun公开了其Java虚拟机规范),也就有了BEA的 JRockit,IBM的Jikes,Sun的Hotspot。

  Java的核心有两点,Java类加载(Java Class Loader)和Java内存管理,它们具体体现在Java类库的以下几个类:

  java.lang.ClassLoader(java.lang.Class):我们调用的类,包括其接口和超类,import的类是怎么被Java虚拟机载入的?为什么static的字段在servlet容器里面可以一直生存下去(Spring容器中)?

  java.lang.Thread(java.lang.ThreadLocal):垃圾回收是怎么进行的(垃圾回收线程)?我们的程序是怎么退出的?

  java.lang.refelect.Proxy(java.lang.refelect.Method):为什么Tomcat、 Tapestry、Webwork、Spring等容器和框架可以通过配置文件来调用我们写的类?Servlet规范、JSF规范、EJB规范、JDBC 规范究竟是怎么回事?为什么它们几乎都是一些接口,而不是具体类?

  Servlet线程安全

  在Java的server side开发过程中,线程安全(Thread Safe)是一个尤为突出的问题。因为容器,如Servlet、EJB等一般都是多线程运行的。虽然在开发过程中,我们一般不考虑这些问题,但诊断问题 (Robust),程序优化(Performance),我们必须深入它们。

  什么是线程安全?

  引用

  Thread-safe describes a program portion or routine that can be called from multiple programming threads without unwanted interaction between the threads。

  在Java里,线程安全一般体现在两个方面:

  1、多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关键字synchronized。如 ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)。如果你在 interator一个List对象时,其它线程remove一个element,问题就出现了。

  2、每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类,而没有Java关键字支持,如像static、transient那样。

  一个普遍的疑问,我们的Servlet中能够像JavaBean那样declare instance或static字段吗?如果不可以?会引发什么问题?

  答案是:不可以。我们下面以实例讲解:

  首先,我们写一个普通的Servlet,里面有instance字段count:

  web.xml 》》

  1 《servlet》

  2 《servlet-name》SimpleServlet《/servlet-name》

  3 《servlet-class》servlet.SimpleServlet《/servlet-class》

  4 《/servlet》

  5 《servlet-mapping》

  6 《servlet-name》SimpleServlet《/servlet-name》

  7 《url-pattern》/SimpleServlet《/url-pattern》

  8 《/servlet-mapping》

  SimpleServlet 》》

  1 public class SimpleServlet extends HttpServlet {

  2 private int counter = 0;

  3 @Override

  4 protected void service(HttpServletRequest request, HttpServletResponse response)

  5 throws ServletException, IOException {

  6 response.getWriter().println(“《HTML》《BODY》”);

  7 response.getWriter().println(this + “ ==》 ”);

  8 response.getWriter().println(Thread.currentThread() + “: 《br》”);

  9 for(int c=0;c《10;c++){

  10 response.getWriter().println(“Counter = ” + counter + “《BR》”);

  11 try {

  12 Thread.sleep(1000);

  13 counter++;

  14 } catch (Exception e) {

  15 e.printStackTrace();

  16 }

  17 }

  18 response.getWriter().println(“《/BODY》《/HTML》”);

  19 }

  20 }

  test.html 》》

  1 《HTML》

  2 《BODY》

  3 《TABLE》

  4 《TR》

  5 《TD》《IFRAME src=“SimpleServlet” name=“servlet1” height=“200%”》 《/IFRAME》《/TD》

  6 《/TR》

  7 《/TABLE》

  8 《/BODY》

  9 《/HTML》

  大家应该发现,test.html写的和zwchen的博客原文中的写的有点区别,本来也是按照zwchen的博客原文中的去测试的,但是相信很多人并没有得出理想的结果,正如博客下面评论上5楼所说的:“没有出现线程安全问题,数字的顺序都是正确的”,我也是如此(我用的是Firefox浏览器)。后来换了IE浏览器进行测试出现下面的问题,在页面上只显示出了第一个《tr》《/tr》里面的内容,于是我的处理方法就是:test.html的内容如上所示,打开3个IE浏览器,同时在浏览器中输入:

  a: http://localhost:8080/ServletTest/SimpleServlet

  b: http://localhost:8080/ServletTest/SimpleServlet

  c: http://localhost:8080/ServletTest/SimpleServlet

  测试结果如下:

  深入研究Servlet线程安全性问题

  我们会发现三点:

  1、Servlet是一个单例对象(Singleton),因为我们看到多次请求的this指针所有打印出来的hashCode值都相同。

  2、servlet在不同的线程(线程池)中运行,如http-8080-1,http-8080-2,http-8080-3 等输出值可以明显区分出不同的线程执行了不同一段Servlet逻辑代码。

  3、count变量在不同的线程中共享,而且它的值被不同的线程修改,输出时已经不是顺序输出。也就是说,其他的线程会篡改当前线程中实例变量的值,针对这些对象的访问不是线程安全的。

  上面的结果,违反了线程安全的两个方面。

  那么,我们怎样保证按照我们期望的结果运行呢?首先,我想保证产生的count都是顺序执行的。

  我们将Servlet代码重构如下:

  1 public class SimpleServlet extends HttpServlet {

  2 private int counter = 0;

  3 private String mutex = “”;

  4 @Override

  5 protected void service(HttpServletRequest request, HttpServletResponse response)

  6 throws ServletException, IOException {

  7 response.getWriter().println(“《HTML》《BODY》”);

  8 response.getWriter().println(this + “ ==》 ”);

  9 response.getWriter().println(Thread.currentThread() + “: 《br》”);

  10 synchronized (mutex){

  11 for(int c=0;c《10;c++){

  12 response.getWriter().println(“Counter = ” + counter + “《BR》”);

  13 try {

  14 Thread.sleep(1000);

  15 counter++;

  16 } catch (Exception e) {

  17 e.printStackTrace();

  18 }

  19 }

  20 }

  21 response.getWriter().println(“《/BODY》《/HTML》”);

  22 }

  23 }

  这符合了我们的要求,输出都是按顺序的,这正式synchronized的含义。

分享到:
评论

相关推荐

    深入研究Servlet线程安全性问题.pdf

    ### 深入研究Servlet线程安全性问题 #### 一、引言 Servlet技术作为Java Web开发中的核心组件之一,因其高效性和灵活性被广泛应用于Web应用程序的开发中。Servlet能够处理HTTP请求,并产生相应的响应。它的一个...

    jakarta-servletapi-4-src.zip servlet源码

    通过对jakarta-servletapi-4-src.zip源码的分析,开发者不仅可以加深对Servlet API的理解,还能学习到如何优化Web应用程序的性能、安全性以及用户体验。这是一次深入挖掘Java Web核心技术的宝贵机会,对于任何希望...

    Servlet3-sources-code

    **Servlet3源码解析** ...Servlet3.0是Servlet规范的一个重要版本,引入了许多新特性和改进,大大提升了开发效率和...所以,无论你是初学者还是经验丰富的开发者,研究Servlet3的源码都将是一个非常有价值的学习过程。

    servlet源码

    在深入研究servlet源码之前,我们需要了解Servlet的基本概念: 1. **Servlet生命周期**:Servlet的生命周期包括加载、实例化、初始化、服务、销毁五个阶段。通过`init()`方法初始化,`service()`方法处理请求,`...

    servlet2.5源码

    此外,源码中的异常处理、线程安全、性能优化等方面都是学习的重点,这对于任何Java Web开发者来说都是非常宝贵的经验。同时,源码包中的编译包可以帮助你在项目中直接引用,无需从头编译源码。

    Servlet源码

    7. **安全性** - Tomcat支持多种安全认证机制,如Basic、Digest、Form等。 - SSL/TLS支持,提供加密的HTTPS连接。 8. **错误处理和日志记录** - 错误页面配置和异常处理逻辑。 - 使用`org.apache.catalina....

    servlet源代码

    10. **多线程安全**:Servlet容器为每个请求创建一个单独的线程,因此源代码需要确保Servlet实例是线程安全的,避免共享状态导致的问题。 在“servlet_src”压缩包中深入研究这些源代码,将有助于你更全面地理解...

    深入Java Web心脏:Servlet工作原理全解析

    ### 深入Java Web心脏:Servlet工作原理全解析 #### Java语言概述 Java作为一种流行的编程语言,由Sun Microsystems(现归Oracle所有)于1995年首次发布。其设计初衷是为了满足跨平台性、面向对象编程、健壮性、...

    JavaServlet2.3帮助文档

    通过实现`SingleThreadModel`接口,开发者可以使每个Servlet实例在一个时间只能处理一个请求,以避免线程安全问题。 2. **过滤器(Filter)**:Servlet2.3引入了过滤器概念,这是一个预处理或后处理请求和响应的...

    Java实现登陆案例servlet

    在这个“Java实现登录案例servlet”的示例中,我们将深入理解如何使用Servlet来创建一个简单的用户登录系统。 首先,了解Servlet生命周期是非常重要的。Servlet在服务器启动时被加载,然后等待客户端的请求。当请求...

    j2ee Servlet源码

    通过研究Servlet源码,我们可以更深入地理解这些概念的实现细节,包括请求分发、线程管理、生命周期控制等。这对于提升Web应用性能优化、问题排查以及设计高效、健壮的Java Web系统有着重要意义。同时,源码学习也能...

    servlet包源码

    4. **Servlet源码分析**:通过阅读Servlet的源码,开发者可以理解Servlet的内部工作机制,比如请求处理流程、Servlet的线程安全性、以及Servlet如何与Web容器交互。例如,HttpServlet类中的`doGet()`和`doPost()`...

    servlet2.4和jsper2.0的源代码

    3. **Servlet的多线程模型**:Servlet容器会在多个线程中调用同一个Servlet实例的服务方法,因此开发者需要注意线程安全问题。 4. **过滤器(Filter)**:Servlet 2.4引入了过滤器的概念,允许在请求到达Servlet...

    servlet-api-private.zip

    深入研究"servlet-api-private.zip"中的私有实现,我们可以了解到Servlet容器如何解析和执行Servlet代码,以及如何处理线程安全、性能优化、错误处理等问题。例如,私有实现可能涉及到线程池的管理,以提高并发性能...

    java+servlet的企业网站源码

    11. 安全性:企业网站必须考虑安全性,包括防止SQL注入、跨站脚本攻击(XSS)、跨站请求伪造(CSRF)等。Spring Security、Apache Shiro等框架提供了一整套解决方案,用于实现身份验证、授权和安全控制。 这个"java...

Global site tag (gtag.js) - Google Analytics