理解Servlet的生命周期的重点:非线程安全

初学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.

相关推荐