`
ajax_xu
  • 浏览: 156193 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

servlet/jsp线程安全

 
阅读更多
转自  http://www.newasp.net/tech/java/15350.html
在编写Servlet/JSP程序时,对实例变量一定要小心使用。因为实例变量是非线程安全的。
在Servlet/JSP中,变量可以归为下面的几类:

1. 类变量
request,response,session,config,application,以及JSP页面内置的page, pageContext。
其中除了application外,其它都是线程安全的。

2. 实例变量
实例变量是实例所有的,在堆中分配。在Servlet/JSP容器中,一般仅实例化一个Servlet/JSP实例,
启动多个该实例的线程来处理请求。而实例变量是该实例所有的线程所共享,所以,实例变量不是线程安全的。

3. 局部变量
局部变量在堆栈中分配,因为每一个线程有自己的执行堆栈,所以,局部变量是线程安全的。

二、在Servlet/JSP中的多线程同步问题


在JSP中,使用实例变量要特别谨慎。首先请看下面的代码:
// instanceconcurrenttest.jsp
<%@ page contentType="text/html;charset=GBK" %>
<%!
    //定义实例变量
    String username;
    String password;
    java.io.PrintWriter output;
%>
<%
    //从request中获取参数
    username = request.getParameter("username");
    password = request.getParameter("password");
    output = response.getWriter();
    showUserInfo();    
%>
<%!
    public void showUserInfo() {
        //为了突出并发问题,在这儿首先执行一个费时操作
        int i =0;
        double sum = 0.0;
        while (i++ < 200000000) {
            sum += i;
        }
        
        output.println(Thread.currentThread().getName() + "<br>");
        output.println("username:" + username + "<br>");
        output.println("password:" + password + "<br>");
    }
%>

在这个页面中,首先定义了两个实例变量,username和password。然后在从request中获取这两个参数,并调用showUserInfo()方法将请求用户的信息回显在该客户的浏览器上。在一个用户访问是,不存在问题。但在多个用户并发访问时,就会出现其它用户的信息显示在另外一些用户的浏览器上的问题。这是一个严重的问题。为了突出并发问题,便于测试、观察,我们在回显用户信息时执行了一个模拟的费时操作,比如,下面的两个用户同时访问(可以启动两个IE浏览器,或者在两台机器上同时访问):
a:    http://localhost:8080/instanceconcurrenttest.jsp?username=a&password=123
b:    http://localhost:8080/instanceconcurrenttest.jsp?username=b&password=456


如果a点击链接后,b再点击链接,那么,a将返回一个空白屏幕,b则得到a以及b两个线程的输出。请看下面的屏幕截图:

图1:a的屏幕

图2:b的屏幕

从运行结果的截图上可以看到,Web服务器启动了两个线程分别来处理来自a和b的请求,但是在a却得到一个空白的屏幕。这是因为上面程序中的output, username和password都是实例变量,是所有线程共享的。在a访问该页面后,将output设置为a的输出,username,password分别置为a的信息,而在a执行printUserInfo()输出username和password信息前,b又访问了该页面,把username和password置为了b的信息,并把输出output指向到了b。随后a的线程打印时,就打印到了b的屏幕了,并且,a的用户名和密码也被b的取代。请参加下图所示:


图3:a、b两个线程的时间线

而实际程序中,由于设置实例变量,使用实例变量这两个时间点非常接近,所以,像本例的同步问题并没有这么突出,可能会偶尔出现,但这却更加具有危险性,也更加难于调试。

同样,对于Servlet也存在实例变量的多线程问题,请看上面页面的Servlet版:
// InstanceConcurrentTest.java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.PrintWriter;
public class InstanceConcurrentTest extends HttpServlet 
{
    String username;
    String password;
    PrintWriter out;
    public void doGet(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException,java.io.IOException
    {
        //从request中获取参数
        username = request.getParameter("username");
        password = request.getParameter("password");
        System.out.println(Thread.currentThread().getName() + 
                     " | set username:" + username);
        out = response.getWriter();
        showUserInfo();    
    }
    public void showUserInfo() {
        //为了突出并发问题,在这儿首先执行一个费时操作
        int i =0;
        double sum = 0.0;
        while (i++ < 200000000) {
            sum += i;
        }
        out.println("thread:" + Thread.currentThread().getName());
        out.println("username:"+ username);
        out.println("password:" + password);
    }
}


三、解决方案


1. 以单线程运行Servlet/JSP


在JSP中,通过设置:<%@ page isThreadSafe="false" %>,在Servlet中,通过实现javax.servlet.SingleThreadModel,此时Web容器将保证JSP或Servlet实例以单线程方式运行。

重要提示:在测试中发现,Tomcat 4.1.17不能正确支持isThreadSafe属性,所以,指定isTheadSafe为false后,在Tomcat 4.1.17中仍然出现多线程问题,这是Tomcat 4.1.17的Bug。在Tomcat 3.3.1和Resin 2.1.5中测试通过。

2. 去除实例变量,通过参数传递


从上面的分析可见,应该在Servlet/JSP中尽量避免使用实例变量。比如,下面的修正代码,去除了实例变量,通过定义局部变量,并参数进行传递。这样,由于局部变量是在线程的堆栈中进行分配的,所以是线程安全的。不会出现多线程同步的问题。代码如下:
<%@ page contentType="text/html;charset=GBK" %>
<%
    //使用局部变量
    String username;
    String password;
    java.io.PrintWriter output;
    //从request中获取参数
    username = request.getParameter("username");
    password = request.getParameter("password");
    output = response.getWriter();
    showUserInfo(output, username, password);    
%>
<%!
    public void showUserInfo(java.io.PrintWriter _output, 
         String _username, String _password) {
        //为了突出并发问题,在这儿首先执行一个费时操作
        int i =0;
        double sum = 0.0;
        while (i++ < 200000000) {
            sum += i;
        }
        
        _output.println(Thread.currentThread().getName() + "<br>");
        _output.println("username:" + _username + "<br>");
        _output.println("password:" + _password + "<br>");
    }
%>


注:有的资料上指出在printUserInfo()方法或者实例变量的相关操作语句上使用synchronized关键字进行同步,但这样并不能解决多线程的问题。因为,这样虽然可以使对实例变量的操作代码进行同步,但并不能阻止一个线程使用另外一个线程修改后的“脏的”实例变量。所以,除了降低运行效率外,不会起到预期效果。
分享到:
评论

相关推荐

    servlet线程安全问题

    Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常细致地考虑多线程的安全性问题。 Servlet 的多线程机制是建立在 Java 多线程机制之上的。Servlet 容器会自动使用线程池等技术来支持...

    servlet/jsp 入门教程

    - **高效**: 通过轻量级线程模型,Servlet可以快速响应新的请求,且不需要为每次请求都加载新的实例。 - **方便**: 内置了许多实用工具方法,如解析HTML表单数据、操作HTTP头、处理Cookie和管理会话状态等。 - **...

    达内servlet/jsp/jquery资料

    - 在多线程环境中,确保Servlet的线程安全非常重要。 - 通常采用同步机制(如`synchronized`关键字)、无状态设计等方式来保证线程安全。 ### 27. 中文问题(2) - 进一步讨论中文处理的问题,包括数据库连接、...

    ACCP6.0 S2 使用JSP/Servlet/Ajax技术开发新闻发布系统

    2. Servlet生命周期:初始化、服务、销毁三个阶段,以及Servlet配置和多线程处理。 3. 请求和响应对象的使用:如何从请求中获取参数,如何设置响应头和正文。 4. 数据库操作:使用JDBC连接数据库,执行SQL查询,处理...

    jsp day1技术解析(servlet/jsp)课件下载

    - `isThreadSafe`:线程安全标志。 - `isELIgnored`:是否忽略表达式语言。 - `contentType`:设置内容类型。 - `pageEncoding`:设置页面编码。 例如: ```jsp *" session="true" %&gt; ``` ##### 3.4 包含与...

    Servlet与JSP之间的关系

    然后,服务器将请求信息转发给Servlet,并为每个请求创建一个新的执行线程;Servlet处理请求,生成响应并返回给服务器,最后由服务器将响应送回客户端。 Servlet的生命周期包括三个阶段:初始化、执行和终止。初始...

    Servlet+JSP购物车

    - **并发问题**:在高并发环境下,确保多个线程同时访问Session时的安全性,可能需要采用同步机制或其他并发控制策略。 5. **性能优化**: - **避免过多Session数据**:减少Session中的数据量可以降低服务器内存...

    Servlet与JSP核心编程(第2卷 第2版)

    - 异步处理能力,允许Servlet在处理请求时释放线程,提高并发性能。 11. **JSP 2.0新特性** - 更强大的EL表达式,支持方法调用和类型转换。 - 自动脚本元素,无需显式声明`和`%&gt;`,提高JSP代码的可读性。 以上...

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

    ### servlet与Struts action线程安全问题分析 #### 一、引言 随着互联网应用的不断普及和发展,基于Servlet和JSP技术的应用程序在企业级应用中占据着重要的位置。Servlet作为一种常用的技术栈,因其多线程运行特性...

    Java - Servlet and JSP Quick Reference

    - **高效性:**Servlets使用线程而非操作系统进程,这意味着每个请求不会创建一个新的实例,而是由现有的Servlet实例处理,这极大地提高了效率。 - **便利性:**Servlets提供了大量的高级实用工具,如数据库访问、...

    Servlet和JSP学习指南.pdf

    4. **多线程**:Servlet默认在一个线程中服务每个请求,因此需要考虑线程安全问题,特别是在共享数据时。 JSP(JavaServer Pages)是另一种Java Web技术,它允许开发者将HTML代码与Java代码混合编写,以生成动态...

    Java+Servlet和JSP教程

    - **高效性**:Servlet 在服务器端以 Java 线程的形式运行,相比于 CGI 每次请求都需要启动新的进程,Servlet 可以极大地减少进程启动带来的开销。此外,多个请求可以通过多个线程处理,而不需要多次加载相同的 ...

    java web教程(servlet,jsp,jdbc)java高手真经

    Servlet容器(如Tomcat、Jetty)负责管理Servlet实例,提供线程安全的环境,并自动处理客户端请求。 JSP是Servlet的另一种表现形式,它更注重视图层的开发,允许开发者使用HTML、CSS和Java脚本语言来编写页面。JSP...

    jsp&servlet电子书

    在本书中,读者会学习到Servlet的生命周期、服务方法、多线程处理、Servlet配置以及Servlet容器的工作原理。此外,书中还会介绍如何使用Filter和Listener来增强应用程序的功能,比如日志记录、会话管理等。 在JSP与...

    Servlet+Jsp+技术概述

    Servlet和JSP运行在Web容器(如Tomcat、Jetty等)中,这些容器提供了运行Java Web应用所需的环境,包括Servlet和JSP的生命周期管理、线程管理以及与HTTP协议的交互。 **总结** Servlet和JSP是Java Web开发的重要...

    javax.servlet3.0 javax.servlet_jsp2.0源码

    【标题】"javax.servlet3.0 javax.servlet_jsp2.0源码"涉及的是Java Servlet和JSP(JavaServer Pages)技术的核心API,这两个组件是Java Web开发的基础。源码分析可以帮助我们深入理解它们的工作原理。 【描述】...

    美河图书提供.Servlet与JSP核心编程第二版_带目录

    - **多线程处理**:Servlet容器默认为每个请求分配独立的线程,理解如何正确处理线程安全问题对于提高应用性能至关重要。 通过以上知识点的介绍,《Servlet与JSP核心编程第二版》涵盖了Servlet和JSP技术的基本原理...

    Servlet与JSP核心编程第二版(含书签)

    - **异步处理**:Servlet 3.0引入了异步处理的支持,使得Servlet能够处理长时间运行的任务而不会阻塞服务器线程。 #### 三、JSP技术详解 - **JSP基本语法**: - 脚本元素(Scriptlets):如`&lt;% %&gt;`用来执行Java...

    jsp线程学习上课代码

    本"jsp线程学习上课代码"的资源可能是为了教授如何在JSP中使用和管理线程,帮助开发者深入理解这一主题。源码的提供是为了让学生或初学者能够实际操作,从而更好地理解和学习。下面将详细探讨JSP与线程的相关知识点...

Global site tag (gtag.js) - Google Analytics