如果在编写Servlet /JSP 程序时不注意多线程同步问题,这往往造成编写的程序在少量用户访问时没有任何问题,而在并发用户上升到一定的值时,经常会出现一些问题,很难调试。
当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误;
并发模拟例子:
form.jsp
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'form.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action ="HelloWorld" >
username:<input type ="text" name ="username"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
package com.wyy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
private String username;
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.username = req.getParameter("username");
//运行一些后端的业务处理
try {
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
req.setAttribute("username",username);
req.getRequestDispatcher("hello.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
hell.jsp
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'hello.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
username:
<%=request.getAttribute("username") %>
</body>
</html>
测试:启动两个浏览器,模拟两个线程:
火狐浏览器:输入http://localhost:8080/webtest/form.jsp
Chrome:输入http://localhost:8080/webtest/form.jsp
先提交火狐:再提交chrome
结果:
火狐:
username:lisi
chrome:
username:lisi
Servlet 的多线程同步问题:Servlet本身是单实例的,这样当有多个用户访问某个Servlet时,会访问该唯一的Servlet实例中的成员变量,
如果对成员变量进行些操作,那就睡导致Servlet的多线程问题,即数据不一致;
解决办法:
(1)、把private String username 放在doGet方法内,即去除实例变量,使用局部变量(最好的解决办法);
(2)、使用同步代码块
synchronized{............}