`
iamlotus
  • 浏览: 108102 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Tomcat的Request不是线程安全的

 
阅读更多

这几天遇到一个和Request及Cookie相关的问题,再次验证了多线程是魔鬼的道理。


这是从一个第三方jar中反编译出来的代码,程序间歇性的抛NullPointerException,看stack trace是第8行抛的。

 

  public String getCookieVal(String key)
   {
     Cookie[] cookies = this.req.getCookies();
     if (cookies == null) {
       return null;
     }
     for (int i = 0; i   if (key.equals(cookie.getName())) {
         return cookie.getValue();
       }
     }
     return null;
 

这行为什么会抛NullPointerException呢?看代码无非key是null或者cookie是null,于是想到加Log,捕捉到NPE时打出传入的key和所有的cookie,看看有没有null。

可是... 从Log看,捕捉到NPE时,key是有值的,所有的cookie也都是有值的,难道见鬼了?


这个bug纠结了两天,才找到了问题所在:正如标题,Request.getCookies()不是线程安全的

先来看一下Servelt 2.4 spec 中关于Request.getCookies()的描述

 写道
getCookies()
public Cookie[] getCookies()
Returns an array containing all of the Cookie objects the client sent with this
request. This method returns null if no cookies were sent.

Returns: an array of all the Cookies included with this request, or null if
the request has no cookies

 



  从这个描述来,getCookies方法返回的数组中应该不会包含null才对,但是,按照Tomcat 5.5的源码来看,并不是这样,

以下是org.apache.catalina.connector.Request中的实现

  /*
     * Return the set of Cookies received with this Request.
     */
    public Cookie[] getCookies() {

        if (!cookiesParsed)
            parseCookies();

        return cookies;

    }

 parseCookies源码如下

 
/**
     * Parse cookies.
     */
    protected void parseCookies() {

        cookiesParsed = true;

        Cookies serverCookies = coyoteRequest.getCookies();
        int count = serverCookies.getCookieCount();
        if (count <= 0)
            return;

        cookies = new Cookie[count];

        int idx=0;
        for (int i = 0; i < count; i++) {
            ServerCookie scookie = serverCookies.getCookie(i);
            try {
                /*
                we must unescape the '\\' escape character
                */
                Cookie cookie = new Cookie(scookie.getName().toString(),null);
                int version = scookie.getVersion();
                cookie.setVersion(version);
                cookie.setValue(unescape(scookie.getValue().toString()));
                cookie.setPath(unescape(scookie.getPath().toString()));
                String domain = scookie.getDomain().toString();
                if (domain!=null) cookie.setDomain(unescape(domain));//avoid NPE
                String comment = scookie.getComment().toString();
                cookie.setComment(version==1?unescape(comment):null);
                cookies[idx++] = cookie;
            } catch(IllegalArgumentException e) {
                // Ignore bad cookie
            }
        }
        if( idx < count ) {
            Cookie [] ncookies = new Cookie[idx];
            System.arraycopy(cookies, 0, ncookies, 0, idx);
            cookies = ncookies;
        }

    }

  

getCookies() 的流程是先置cookiesParsed,再生成并初始化cookies[]数组,并且没有同步,所以,若两个线程在同一个Request上按照以下时间步骤调用getCookies方法,就会出错,


Thread A                                         Thread B



1 getCookies();


2 parseCookie();


3 cookiesParsed = true;


4 cookies = new Cookie[count];



5                                                       getCookies();


6                                                      cookiesParsed==true;


7                                                       return cookies;




如果cookies 数组刚被new 出来,还没有完成初始化,ThreadA就让出了时间片,ThreadB接着调用getCookies()就可能读到一个没有初始化完成的cookies,这个cookies中包含null元素。



这同时也解释了为什么ThreadB的log中request.getCookies()都是有值的:当ThreadB的log再次调用request.getCookies()时,ThreadA早已再次获得时间片,并完成了cookies的初始化,所以log中getCookies()拿到的都是好的发现不了问题。



定位了问题后,解决方法是从源头上避免对于request的并发访问,相对就比较容易实现了。



这次debug的总结


1) Request并不是一个线程安全的对象

2) 多线程真心坑爹,要不java也不会推出个conccurent包。每次遇到多线程的问题,由于不能固定重现,大多数时间都是在脑海中画时序图,猜哪条路径会出错,太坑爹了!

 

0
1
分享到:
评论
6 楼 iamlotus 2014-05-08  
wangheng1700 写道
还有线程内部的堆栈都是单独的,在调各自的Request.getCookie方法的时候会各自创建运行时的线程堆栈,怎么会出现B线程去取A线程未初始化的变量呢?

这个是第三方包,会从Request中取cookie信息单独开个线程进行其它的操作。
5 楼 wangheng1700 2013-06-27  
还有线程内部的堆栈都是单独的,在调各自的Request.getCookie方法的时候会各自创建运行时的线程堆栈,怎么会出现B线程去取A线程未初始化的变量呢?
4 楼 wangheng1700 2013-06-27  
同意3楼的看法,怎么可能多个线程里面访问同一个Request!!!!楼主给个说法!
3 楼 alan0509 2012-01-11  
这 代码 我怎么看着 有问题 。求解?


为什么 会出现 若两个线程在同一个Request ?
2 楼 ruby_windy 2011-10-13  
很好的分析.
1 楼 zhufeng1981 2011-10-12  
分析的很精彩。

相关推荐

    Servlet线程安全问题.docx

    当第一个请求到达时,Tomcat会创建Servlet实例,后续的请求将复用同一个实例,这意味着多个线程可能同时访问Servlet的同一实例,从而引发线程安全问题。 Servlet线程池机制 Tomcat容器为了高效地处理请求,采用了一...

    tomcat6源码分析

    Tomcat6支持多种安全认证方式,如Basic认证、Form认证等,并提供了安全管理器和角色权限控制,以实现Web应用的安全访问。 七、性能优化 1. 端口绑定:可以通过配置多个Connector来优化不同类型的请求处理。 2. 配置...

    servlet+tomcat面试题及答案解析.rar

    Tomcat使用工作线程模型,当请求到达时,会从线程池中取出一个线程来处理请求,处理完后再放回线程池。 10. **解释Servlet的doGet()和doPost()方法?** 这两个方法是Servlet处理HTTP请求的核心,doGet()用于处理...

    tomcat服务器工作原理

    通过`conf/server.xml`和`conf/context.xml`,Tomcat可以设置用户认证、角色权限以及SSL/TLS加密等安全措施。 8. **错误处理** Tomcat在遇到异常时,会根据配置生成错误页面,并记录在日志文件中,帮助开发者诊断...

    How Tomcat Works 中文版

    Tomcat5的后台处理线程是共享的,相比于Tomcat4中每个组件都有自己的处理线程,Tomcat5能更高效地利用资源。此外,Tomcat5在代码上简化了映射组件的使用。 这本书共分为20章,其中前两章作为引言,为读者提供预备...

    How Tomcat Works 中文版.pdf

    比如,Tomcat 5支持更高版本的Servlet和JSP规范,具有更高效的默认连接器,以及后端处理线程的共享优化,从而减少了资源消耗。此外,Tomcat 5还简化了查找子组件的映射组件,使得代码结构更加简洁。 全书共分为20章...

    tomcat源码

    Tomcat采用多线程模型处理请求,包括基于线程池的Executor和NIO的非阻塞I/O。Executor线程池可以预先配置线程数量,有效控制资源消耗;NIO模式则利用Java的Selector机制,提高并发处理能力。 5. **安全性** ...

    tomcat5.5源代码

    `org.apache.coyote`包下包含了Tomcat的连接器实现,如`CoyoteAdapter`,它负责将网络I/O事件转化为内部请求对象,如`org.apache.coyote.Request`和`org.apache.coyote.Response`。这部分源码展示了Tomcat如何处理...

    Tomcat源码学习:一个最简单的“Tomcat”

    8. **安全性**:Tomcat的安全配置,如 Realm、Role和Constraint,以及如何保护Web应用免受攻击。 通过这个“SimpleTomcat”项目,读者可以逐步了解这些核心概念,并通过实践加深理解。这对于想要深入研究Web服务器...

    深入剖析Tomcat+源码.zip

    2. **Executor线程池**:Tomcat使用Executor接口来管理处理请求的线程,这允许系统根据需求动态调整线程数量,以优化资源利用和并发性能。 3. **Request Processor**:每个请求都会被分配一个Request Processor,它...

    tomcat的一些详细介绍

    Tomcat 详细介绍 Tomcat 是一个开源的 Web 服务器和 Servlet 容器,...Tomcat 的配置文件 server.xml 是 Tomcat 的核心配置文件,用于配置 Tomcat 的各种设置,如服务名、端口号、处理请求的线程数、超时时间等等。

    tomcat-7.0.42源码

    7.0.42版本是Tomcat发展中的一个重要里程碑,它在性能、稳定性和安全性上都有显著提升。本文将从多个角度深入探讨Tomcat 7.0.42的源码,揭示其内部工作原理。 一、架构概述 Tomcat的架构基于组件模型,主要由四个...

    tomcat源码基于6.0

    Tomcat的安全配置涉及SSL/TLS配置、用户认证、访问控制等多个方面。正确配置`server.xml`中的`&lt;Connector&gt;`和`&lt;Realm&gt;`元素,可以实现安全的HTTPS通信和权限管理。 总结,Tomcat 6.0源码分析是一个深入了解Java Web...

    译How Tomcat Works(第二章)

    5. **线程模型**:Tomcat使用多线程处理请求,每个请求都会分配一个工作线程来执行。线程池管理和调度确保了高并发情况下的性能和效率。 6. **部署和配置**:Tomcat支持多种方式部署Web应用,如WAR文件或解压后的...

    How Tomcat Work.pdf

    在介绍Tomcat的两个主要版本时,书中指出Tomcat5相较于Tomcat4有所改进,支持了更先进的Servlet2.4和JSP2.0规范,拥有更有效率的默认连接器,并且共享后台处理线程,大大减少了资源消耗。Tomcat5不需要映射组件来...

    Tomcat中一种半通用回显方法 - 先知社区1

    【标题】:“Tomcat中一种半通用回显方法 - 先知社区1”涉及的知识点主要围绕Java Web服务器Tomcat的内部工作原理以及利用反射技术进行回显操作。以下是详细解释: 1. **Tomcat与HTTP响应**: Tomcat是Apache软件...

    how tomcat works 中文版及源码

    8. **安全性**:Tomcat提供了多种安全措施,如SSL/TLS加密、角色基础的访问控制、FORM和BASIC认证等。 9. **源码分析**:书中提供的源码可以帮助开发者了解Tomcat如何处理请求、调度线程、管理会话等。例如,你可以...

Global site tag (gtag.js) - Google Analytics