Servlet的线程安全性

首先说明Servlet并非线程安全,在所有对某个url的请求全部被转发到同一个Servlet。所以在开发Servlet过程尽量不使用实例变量,而使用局部变量。虽然在整个应用过程中只有一个Servlet,但是Servlet并不是单例模式,Server只是被容器创建一次,所以才只存在一个Servlet实例。
下面简单介绍Servlet请求过程主要参考stackoverflow的一个回答:

当容器启动时候:

1.读取web.xml文件,根据Servlet的 load-on-startup属性确定是否启动,非负时候Servlet会被启动。
2.获得Servlet的路径
3.加载并且实例化Servlet,只实例化一次

就如下面:

String urlPattern = parseWebXmlAndRetrieveServletUrlPattern();
String servletClass = parseWebXmlAndRetrieveServletClass();
HttpServlet servlet = (HttpServlet) Class.forName(servletClass).newInstance();
servlet.init();
servlets.put(urlPattern, servlet); // Similar to a map interface.

Those Servlets are stored in memory and reused every time the request URL matches the Servlet's associated url-pattern. The servlet container then executes code similar to:

这些Servlet被加载到内存,每次请求都会使用相同的servlet。容器将会执行如下代码:

for (Entry<String, HttpServlet> entry : servlets.entrySet()) {
    String urlPattern = entry.getKey();
    HttpServlet servlet = entry.getValue();
    if (request.getRequestURL().matches(urlPattern)) {
        servlet.service(request, response);
        break;
    }
}

service根据请求类型(post,get)转发到相应方法doGet,doPost;
Servlet容器对于相同的请求使用的是同一Servlet,即Servlet被共享给每个request。所以应该注意:

1.不用实例变量。
2.不要在Servlet的方法加  synchronized关键字.
public class MyServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!!所有请求共享
        thisIsThreadSafe = request.getParameter("foo"); // OK,  线程安全
    } 
}

通过实现SingleThreadModel接口,可以每次请求都创建一个新的Servlet,但由于静态变量和session,依然存在线程安全性问题。所以此接口不推荐使用。
session不是线程安全的,当同一用户在不同浏览器请求时候可能导致线程问题。

Struts的线程安全

1.Struts1不是线程安全的;Struts1的Action是单例模式,每次请求使用一个Action。Struts1太老,认为创建新的实例花费太大,所以使用单例模式。
2.Struts2是线程安全的;每次请求创建新的Action,线程安全。

posted on 2014-10-11 14:43  好好先生耶  阅读(147)  评论(0)    收藏  举报