`
ajax_xu
  • 浏览: 153403 次
  • 性别: 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 容器会自动使用线程池等技术来支持...

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

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

    Servlet+JSP购物车

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

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

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

    Servlet和JSP学习指南.pdf

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

    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开发的基础。源码分析可以帮助我们深入理解它们的工作原理。 【描述】...

    jsp线程学习上课代码

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

    吉林大学珠海学院JSP&Servlet学习笔记(第二版)课后答案

    第四章至第九章涵盖了JSP和Servlet的高级主题,包括JSP的自定义标签、Servlet的过滤器、JSP和Servlet的安全机制等。 本资源为JSP和Servlet学习提供了系统的指导和参考,适合初学者和中级开发者学习和参考。 知识点...

    韩顺平servlet和jsp课堂笔记

    6. **多线程**:每个请求都会创建一个新的Servlet实例,因此Servlet天生支持多线程,需要注意同步问题以避免数据竞争。 【JSP知识点】: 1. **JSP简介**:JSP是一种动态网页技术,将HTML、CSS、JavaScript与Java...

    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+Servlet+and+Jsp+Cookbook.2004.rar

    - **线程安全**:理解Servlet并发问题,避免潜在的线程安全问题。 - **性能监控**:通过Servlet容器提供的监控工具分析应用性能。 6. **开发工具与框架**: - **IDE集成**:如Eclipse、IntelliJ IDEA等,提供了...

    中文版JSP&SERVLET教程

    - **Page Directive的`isThreadSafe`属性**:控制JSP实例是否线程安全。 - **隐含对象的增强**:如`pageContext`, `session`, `request`, `response`等。 7. **Servlet 3.0及以后版本的新特性** - **注解驱动的...

    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 ...

    jsp/servlet基础课件

    每个Servlet实例通常对应一个线程,以处理多个并发请求。 2. **Servlet API:** `javax.servlet`和`javax.servlet.http`包提供了如`HttpServletRequest`、`HttpServletResponse`等接口,用于处理HTTP请求和响应。 ...

    jsp+servlet+javabean实现网上商城项目完整源码

    Servlet的多线程特性保证了其在高并发环境下的性能。 再者,JavaBean是Java语言中的一种设计模式,用于封装数据和提供业务逻辑。在本项目中,JavaBean通常代表商城中的实体对象,如商品、订单、用户等。它们包含了...

    JSP/Servlet Java面试逻辑题

    5、如何现实servlet的单线程模式★★ 答:&lt;%@page isThreadSafe=false%&gt; 面试中会遇到! 6、页面间对象传递的方法 答:request,session,application,cookie等 7、JSP和Servlet有哪些相同点和不同点,他们...

    JSP和Servlet

    本文将详细探讨在使用JSP和Servlet编程时应注意的关键问题,特别是如何确保线程安全。 首先,控制器Servlet的设计应当考虑线程安全性。一个控制器Servlet通常只有一个类的实例,这个实例会被用于处理所有到来的请求...

Global site tag (gtag.js) - Google Analytics