这几天遇到一个和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包。每次遇到多线程的问题,由于不能固定重现,大多数时间都是在脑海中画时序图,猜哪条路径会出错,太坑爹了!
分享到:
相关推荐
当第一个请求到达时,Tomcat会创建Servlet实例,后续的请求将复用同一个实例,这意味着多个线程可能同时访问Servlet的同一实例,从而引发线程安全问题。 Servlet线程池机制 Tomcat容器为了高效地处理请求,采用了一...
Tomcat6支持多种安全认证方式,如Basic认证、Form认证等,并提供了安全管理器和角色权限控制,以实现Web应用的安全访问。 七、性能优化 1. 端口绑定:可以通过配置多个Connector来优化不同类型的请求处理。 2. 配置...
Tomcat使用工作线程模型,当请求到达时,会从线程池中取出一个线程来处理请求,处理完后再放回线程池。 10. **解释Servlet的doGet()和doPost()方法?** 这两个方法是Servlet处理HTTP请求的核心,doGet()用于处理...
通过`conf/server.xml`和`conf/context.xml`,Tomcat可以设置用户认证、角色权限以及SSL/TLS加密等安全措施。 8. **错误处理** Tomcat在遇到异常时,会根据配置生成错误页面,并记录在日志文件中,帮助开发者诊断...
Tomcat5的后台处理线程是共享的,相比于Tomcat4中每个组件都有自己的处理线程,Tomcat5能更高效地利用资源。此外,Tomcat5在代码上简化了映射组件的使用。 这本书共分为20章,其中前两章作为引言,为读者提供预备...
比如,Tomcat 5支持更高版本的Servlet和JSP规范,具有更高效的默认连接器,以及后端处理线程的共享优化,从而减少了资源消耗。此外,Tomcat 5还简化了查找子组件的映射组件,使得代码结构更加简洁。 全书共分为20章...
Tomcat采用多线程模型处理请求,包括基于线程池的Executor和NIO的非阻塞I/O。Executor线程池可以预先配置线程数量,有效控制资源消耗;NIO模式则利用Java的Selector机制,提高并发处理能力。 5. **安全性** ...
`org.apache.coyote`包下包含了Tomcat的连接器实现,如`CoyoteAdapter`,它负责将网络I/O事件转化为内部请求对象,如`org.apache.coyote.Request`和`org.apache.coyote.Response`。这部分源码展示了Tomcat如何处理...
8. **安全性**:Tomcat的安全配置,如 Realm、Role和Constraint,以及如何保护Web应用免受攻击。 通过这个“SimpleTomcat”项目,读者可以逐步了解这些核心概念,并通过实践加深理解。这对于想要深入研究Web服务器...
2. **Executor线程池**:Tomcat使用Executor接口来管理处理请求的线程,这允许系统根据需求动态调整线程数量,以优化资源利用和并发性能。 3. **Request Processor**:每个请求都会被分配一个Request Processor,它...
Tomcat 详细介绍 Tomcat 是一个开源的 Web 服务器和 Servlet 容器,...Tomcat 的配置文件 server.xml 是 Tomcat 的核心配置文件,用于配置 Tomcat 的各种设置,如服务名、端口号、处理请求的线程数、超时时间等等。
7.0.42版本是Tomcat发展中的一个重要里程碑,它在性能、稳定性和安全性上都有显著提升。本文将从多个角度深入探讨Tomcat 7.0.42的源码,揭示其内部工作原理。 一、架构概述 Tomcat的架构基于组件模型,主要由四个...
Tomcat的安全配置涉及SSL/TLS配置、用户认证、访问控制等多个方面。正确配置`server.xml`中的`<Connector>`和`<Realm>`元素,可以实现安全的HTTPS通信和权限管理。 总结,Tomcat 6.0源码分析是一个深入了解Java Web...
5. **线程模型**:Tomcat使用多线程处理请求,每个请求都会分配一个工作线程来执行。线程池管理和调度确保了高并发情况下的性能和效率。 6. **部署和配置**:Tomcat支持多种方式部署Web应用,如WAR文件或解压后的...
在介绍Tomcat的两个主要版本时,书中指出Tomcat5相较于Tomcat4有所改进,支持了更先进的Servlet2.4和JSP2.0规范,拥有更有效率的默认连接器,并且共享后台处理线程,大大减少了资源消耗。Tomcat5不需要映射组件来...
【标题】:“Tomcat中一种半通用回显方法 - 先知社区1”涉及的知识点主要围绕Java Web服务器Tomcat的内部工作原理以及利用反射技术进行回显操作。以下是详细解释: 1. **Tomcat与HTTP响应**: Tomcat是Apache软件...
8. **安全性**:Tomcat提供了多种安全措施,如SSL/TLS加密、角色基础的访问控制、FORM和BASIC认证等。 9. **源码分析**:书中提供的源码可以帮助开发者了解Tomcat如何处理请求、调度线程、管理会话等。例如,你可以...