Servlet的线程安全问题

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

因为架构调整修改一个应用,我在检查QA做压力测试生成的应用日志时,发现一些数据不对劲,于是就开始了三天多的问题排查,从WEB容器 –> 缓存服务器 –> 新增加的代码 –> 压力测试工具,最后找到问题的根源:应用中一个旧的Servlet的代码存在线程安全的BUG。

下面就写一个简单的示例代码来演示一下Servlet的线程安全问题。

一、源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package cn.aofeng.thread;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 一个非线程安全的Servlet演示:<br/>
* 变量<code>name</code>和<code>desc</code>被定义成实例变量。
*
* @author aofeng <a href="mailto:aofengblog@163.com>aofengblog@163.com</a>
*/
public class NotSafeThreadServlet extends HttpServlet {
private static final long serialVersionUID = -2646458389304935967L;
private String name;
private String desc;
public NotSafeThreadServlet() {
super();
}
/**
* The doGet method of the servlet. <br>
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException {
// 接收参数
String paramName = request.getParameter("name");
String paramDesc = request.getParameter("desc");
// 赋值
name = paramName;
desc = paramDesc;
// 休眠
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出响应
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<HTML>");
out.println(" <HEAD><TITLE>NotSafeThreadServlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is [");
out.print(this.toString());
out.println("], using the GET/POST method");
out.print("<br/>paramName=" + paramName);
out.print("paramDesc=" + paramDesc);
out.print("<br/>name=" + name);
out.print("desc=" + desc);
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

二、运行演示。

在两个打开的浏览器上,同时进行请求:
1、第一个浏览器。
请求URL:http://192.168.228.1:8080/AntTest/servlet/NotSafeThreadServlet?name=111&desc=desc_1111 ,响应结果如下:
图1

2、第二个浏览器。
请求URL:http://192.168.228.1:8080/AntTest/servlet/NotSafeThreadServlet?name=222&desc=desc_2222 ,响应结果如下:
图2

从以上的结果可以看出,第二个请求覆盖了第一个请求给实例变量name和desc赋的值。