在Internet中,一个Web应用可能被来自西面八方的客户并发访问(即同时访问),而且有可能这些客户并发访问的是Web应用中的同一个Servlet,Servlet容器为了保证能同时相应多个客户端要求访问的同一个Servlet的HTTP请求,通常会为每个请求分配一个工作线程,这些工作线程并发执行同一个Servlet对象的service()方法。
当多个线程并发执行同一个Servlet对象的service()方法时,可能会导致并发问题。
例:下面的案例用于演示导致并发问题的情形。
public class HelloServlet extends HttpServlet{
public void service(HttpServletRequest request,
HttpServletResponse response)throws ServletException,IOException{
private String username = null;
username = request.getParameter("username");
if(username != null){
username = new String(username.getBytes("ISO-8859-1"),"utf-8");
}
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("您好:" + username);
out.close();
}
}
在HelloServlet中有一个实例变量username,在HelloServlet的service()方法中,先将username请求参数赋值给实例变量username,最后再向客户端返回实例变量username的值,为了延长响应客户请求的时间,在service()方法中调用Thread.sleep(3000)方法睡眠3秒。
在web.xml文件中为HelloServlet映射的URL为"/hello"。同时打开两个浏览器,分别输入如下URL:
http://localhost:8080/helloapp/hello?username=老鼠
http://localhost:8080/helloapp/hello?username=小鸭
以上两个浏览器并发访问HelloServlet,出现了奇怪的现象。在第一个浏览器中,虽然客户端提供的请求参数username的值为"老鼠",HelloServlet却返回"您好:小鸭"。为什么一眨眼,老鼠就变成了鸭呢?
下面在看另一个案例:
public class AdderServlet extends HttpServlet{
public void service(HttpServletRequest request,
HttpServletResponse response)throws ServletException,IOException{
private int sum = 100;
int increase = Integer.parseInt(request.getParameter("increase"));
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println(sum + "+" + increase + "=");
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
sum+=increase;
out.println("sum");
out.close();
}
}
在AdderServlet中有一个实例变量sum,在AdderServlet的service()方法中提取increase请求参数,再把实例变量sum加上increase,最后再向客户端返回实例变量sum的值。为了延长响应客户请求的时间,在service()方法中调用Thread.sleep(3000)方法睡眠3秒钟。
在web.xml文件中为AdderServlet映射的URL为"/adder"。同时打开两个浏览器,分别输入如下URL:
http://localhost:8080/helloapp/adder?increase=100
http://localhost:8080/helloapp/adder?increase=200
两个浏览器并发访问AdderServlet,出现了奇怪的现象。在第二个浏览器中,客户端提供的请求参数increase的值为"200",AdderServlet返回"100+200=400",AdderServlet所做的加法运算显然是错误的。
在解决并发问题时,主要遵循以下原则:
● 根据实际应用需求,合理决定在Servlet中定义的变量的作用于。变量到底为实例变量,还是局部变量,是由实际应用需求决定的。
● 对于多个线程同时访问共享数据而导致并发问题的情况,使用Java同步机制对线程进行同步。
● 不提倡使用被废弃的javax.servlet.SingleThreadModel接口。