初学Java Web 开发的者,常会忽略Servlet的特性:非线程安全。
所谓线程安全就是:每一次调用是独立的结果,不应当受其它调用的影响。从代码上看就是:函数执行中使用的变量都应该是临时的,不应该是全局变量或者实例成员变量,简单的说就是:执行函数必须是无状态执行。
再来说为什么Servlet是非线程安全:因为Servlet的所谓生命周期是由Web服务器的Servlet容器管理的,Web服务器对相同的Servlet 只会实例化一次,也就是说同一个URL地址的多次请求,都是由同一个Servlet的实例在执行。所以,响应请求的函数一定要做到无状态执行。
下面这个例子,演示了一个错误的代码:
public class HelloWorld extends HttpServlet { private PrintWriter pr; private void getWriter(HttpServletResponse resp) throws IOException { pr = resp.getWriter(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { getWriter(resp);//问题所在,产生了状态变量 try { Thread.sleep(9000); } catch (InterruptedException e) { e.printStackTrace(); } out("getQueryString:"+req.getQueryString());//问题所在,使用了状态变量 } private void out(String s) { pr.write(gp(s)); } private String gp(String s) { return "<p>" + s + "<p>"; } }
开两个浏览窗口,先在第一个窗口的请求:http://127.0.0.1/hello?name=mike,
然后再在第二个窗口的请求:http://127.0.0.1/hello?name=jonh
会观察到第一个窗口没有任何输出,第二个窗口输出是:
getQueryString:name=mike getQueryString:name=jonh
之所以出现如此异常现象,就是因为 pr 是一个状态变量,第二次请求时,pr 被改写了,所以每一次请求的输出就定向到第二个窗口了。
servlet 是这样,同理JSP也一样,因为JSP本质上是Servlet.