Servlet之單例與線程安全

[size=medium][b]一、Servlet 是單例嗎[/b][/size]

不是。
1、你可以用多個 URL 映射同一個 Servlet。這樣就會出現多個實例。

2、看看 Servlet 定義:
[quote]
For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration.
如果 servlet 不是在分佈式環境下(默認),servlet 容器必須使一個 servlet 實例對應一個 servlet 聲明。

However, for a servlet implementing the SingleThreadModel(Deprecated) interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.
然而,實現了 SingleThreadModel 接口的 Servlet,可以有多個實例。以處理繁重的請求,並且序列化 request 到特定的 servlet 實例。

public interface SingleThreadModel
Ensures that servlets handle only one request at a time.
[/quote]

[b]結論:[/b]
雖然 Servlet 在多數情況下只有一個實例。但它並不是單例設計模式,即不是真正的單例。


[size=medium][b]二、Servlet 爲什麼是線程不安全的[/b][/size]

基於 JVM 對多線程的支持,這樣可以提高代碼的執行效率。
不需要爲每一個請求都要單獨創建/銷燬 Servlet(執行 init(), desdroy() )。
同一段代碼可以在同一時間被多個請求同時執行。

Servlet 是普通的 Java 類,因此沒有對其做線程安全的處理。


[size=medium][b]三、如何保證線程安全?(避免不安全)[/b][/size]

但是,
Java 的類是線程安全的,只有在一種情況下:該類沒有 instance variables.
即,沒有(實例)變量時。

實例變量(instance properties)是聲明在類中的變量,而不是聲明在方法中的變量。

聲明在方法中的變量是線程安全的,因爲在執行該方法時,每一個線程都會在 Stack 中創建它們各自的變量。
因此,方法中的變量不存在線程不安全問題。



public class ExampleServlet extends HttpServlet {

private Object thisIsNOTThreadSafe;

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

// BAD!! Shared among all requests!
thisIsNOTThreadSafe = request.getParameter("foo");

// OK, this is thread safe.
Object threadSafeObj;
threadSafeObj = request.getParameter("foo");
}
}




[size=medium][b]四、拓展:Struts2 中的 Action 對象是單例嗎?[/b][/size]

不是。
當請求到來時,Web 容器爲每一個請求創建一個 Request 和 Response 對象。
然後再創建一個線程,並把這兩個對象的引用指向該線程。

Struts2 中的 Action 對象是 re-created 的,爲每一個請求。並綁定到 Request 對象上,
作爲 Request 對象的一個屬性。這樣就不存在線程不安全問題,因爲每一個 Request 對象
只綁定一個線程。

注意:不要混淆 Struts DispatcherFilter 和 StrutsAction


-

Why is javax.servlet.[b]SingleThreadModel[/b] deprecated?


The javadoc says why. SingleThreadModel was designed to be an easy solution to low-load concurrency, but it didn't even manage that:
[quote]
Note that SingleThreadModel does not solve all thread safety issues. For example, session attributes and static variables can still be accessed by multiple requests on multiple threads at the same time, even when SingleThreadModel servlets are used. It is recommended that a developer take other means to resolve those issues instead of implementing this interface, such as avoiding the usage of an instance variable or synchronizing the block of the code accessing those resources.
[/quote]
If it can't achieve what it was designed for, it should not be used.


http://stackoverflow.com/questions/2551999
-


-
Why servlets are not thread safe?
http://stackoverflow.com/questions/9555842

How do servlets work? Instantiation, sessions, shared variables and multithreading
http://stackoverflow.com/questions/3106452

Is Servlet singleton?
http://stackoverflow.com/questions/11820840
--


Suppose, I have a webserver which holds numerous servlets. For information passing among those servlets I am setting session and instance variables.

Now, if 2 or more users send request to this server then what happens to the session variables? Will they all be common for all the users or they will be different for each user. If they are different, then how was the server able to differentiate between different users?

One more similar question, if there are n users accessing a particular servlet, then this servlet gets instantiated only the first time the first user accessed it or does it get instantiated for all the users separately? In other words, what happens to the instance variables?


--

ServletContext

When the servlet container (like Apache Tomcat) starts up, it will deploy and load all its web applications. When a web application is loaded, the servlet container creates the ServletContext once and keeps it in the server's memory. The web app's web.xml file is parsed, and each <servlet>, <filter> and <listener> found (or each class annotated with @WebServlet, @WebFilter and @WebListener respectively) is instantiated once and kept in the server's memory as well. For each instantiated filter, its init() method is invoked immediately.

When the servlet container shuts down, it unloads all web applications, invokes the destroy() method of all its initialized servlets and filters, and all ServletContext, Servlet, Filter and Listener instances are trashed.

When a Servlet has a <servlet><load-on-startup> or @WebServlet(loadOnStartup) value greater than 0, its init() method is also immediately invoked during startup. Those servlets are initialized in the same order specified by that value (1 -> 1st, 2 -> 2nd, etc). If the same value is specified for more than one servlet, then each of those servlets is loaded in the order they appear in the web.xml, or @WebServlet classloading. In the event the "load-on-startup" value is absent, the init() method will be invoked whenever the HTTP request hits that servlet for the very first time.

HttpServletRequest and HttpServletResponse

The servlet container is attached to a web server that listens for HTTP requests on a certain port number (port 8080 is usually used during development and port 80 in production). When a client (user with a web browser) sends an HTTP request, the servlet container creates new HttpServletRequest and HttpServletResponse objects and passes them through any defined Filter chain and, eventually, the Servlet instance.

In the case of filters, the doFilter() method is invoked. When its code calls chain.doFilter(request, response), the request and response continue on to the next filter, or hit the servlet if there are no remaining filters.

In the case of servlets, the service() method is invoked. By default, this method determines which one of the doXxx() methods to invoke based off of request.getMethod(). If the determined method is absent from the servlet, then an HTTP 405 error is returned in the response.

The request object provides access to all of the information about the HTTP request, such as its headers and body. The response object provides the ability to control and send the HTTP response the way you want by, for instance, allowing you to set the headers and the body (usually with generated HTML content from a JSP file). When the HTTP response is committed and finished, both the request and response objects are recycled and made for reuse.

HttpSession

When a client visits the webapp for the first time and/or the HttpSession is obtained for the first time via request.getSession(), the servlet container creates a new HttpSession object, generates a long and unique ID (which you can get by session.getId()), and store it in the server's memory. The servlet container also sets a Cookie in the Set-Cookie header of the HTTP response with JSESSIONID as its name and the unique session ID as its value.

As per the HTTP cookie specification (a contract a decent web browser and web server have to adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the Cookie header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check the Cookie header of every incoming HTTP request for the presence of the cookie with the name JSESSIONID and use its value (the session ID) to get the associated HttpSession from server's memory.

The HttpSession stays alive until it has not been used for more than the timeout value specified in <session-timeout>, a setting in web.xml. The timeout value defaults to 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.

On the client side, the session cookie stays alive for as long as the browser instance is running. So, if the client closes the browser instance (all tabs/windows), then the session is trashed on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new HTTPSession to be created, with an entirely new session cookie begin used.

In a nutshell

The ServletContext lives for as long as the web app lives. It is shared among all requests in all sessions.
The HttpSession lives for as long as the client is interacting with the web app with the same browser instance, and the session hasn't timed out at the server side. It is shared among all requests in the same session.
The HttpServletRequest and HttpServletResponse live from the time the servlet receives an HTTP request from the client, until the complete response (the web page) has arrived. It is not shared elsewhere.
All Servlet, Filter and Listener instances live as long as the web app lives. They are shared among all requests in all sessions.
Any attribute that is defined in ServletContext, HttpServletRequest and HttpSession will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as an attribute of its closest matching scope.
Thread Safety

That said, your major concern is possibly thread safety. You should now know that servlets and filters are shared among all requests. That's the nice thing of Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise be too expensive to recreate, init() and destroy() them for every single request.

You should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's not thread-safe! The below example illustrates this:


public class ExampleServlet extends HttpServlet {

private Object thisIsNOTThreadSafe;

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

// BAD!! Shared among all requests!
thisIsNOTThreadSafe = request.getParameter("foo");

// OK, this is thread safe.
Object threadSafeObj;
threadSafeObj = request.getParameter("foo");
}
}


-


轉載請註明,
原文出處:http://lixh1986.iteye.com/blog/2355692


-
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章