Difference between each instance of servlet and each thread of servlet in servlets?
When the Servlet container starts, it:
- reads
web.xml
; - finds the declared Servlets in the classpath; and
- loads and instantiates each Servlet only once.
Roughly, like this:
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:
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;
}
}
The GenericServlet#service()
on its turn decides which of the doGet()
, doPost()
, etc.. to invoke based on HttpServletRequest#getMethod()
.
You see, the servletcontainer reuses the same servlet instance for every request. In other words: the servlets are shared among every request. That's why it's extremely important to write servlet code the threadsafe manner --which is actually simple: just do not assign request or session scoped data as servlet instance variables, but just as method local variables. E.g.
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!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}
Does each request access the same servlet object?
The container will use the same servlet instance if your servlet don't implement SingleThreadModel
.
Otherwise there is no guarantee that the same Servlet
object is hit. The container is free to create more servlet instances if it considers necessary. But the requests comes on different threads, not necessarily newly created (as Sanjay mentioned).
From the Servlet 3.0 specification:
For a servlet not hosted in a distributed environment (the default), the servlet
container must use only one instance per servlet declaration. However, for a servlet
implementing the SingleThreadModel interface, the servlet container may
instantiate multiple instances to handle a heavy request load and serialize requests
to a particular instance.
...
Generally the Web container handles concurrent requests to the same servlet by
concurrent execution of the service method on different threads.
How do servlets instances work?
Only one instance of the servlet is created. This is why it is so important not to include fields in your servlet that are used within the get
and post
methods. A class instance variable (field) within a servlet is not thread safe and could be modified by multiple requests resulting in unexpected behavior.
Consider the following example:
ServletTest.java
@WebServlet("/ServletTest")
public class ServletTest extends HttpServlet {
private static final long serialVersionUID = 1L;
private Integer increment = 0;
public ServletTest() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(increment++);
}
}
test.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="ServletTest" method="post">
<button type="submit">Submit</button>
</form>
</body>
</html>
If you run this example and hit the submit button it will print 0 to the console. Subsequent submits of the form will print 1, then 2, etc... This proves the servlet is the same instance.
Towards understanding servlets and multi-threading
This is not specific to Servlets. This is specific to Java. The Java language and VM supports invoking a single method by multiple threads. Each thread will just have its own share of method-local variables. If you want to restrict this, you've to add a synchronized
lock to the method or to let the servlet implement the (deprecated!) SingleThreadModel
interface. But that's not necessary if you write servlet methods on a threadsafe manner (i.e. do not assign request/session scoped data as instance variable).
Why does the init() method of a servlet run in a different thread?
The servlet is initialized just once by init()
, but for every new request a new thread is created or allocated from a pool to invoke that servlet instance on the appropriate method.
The HttpRequest
and HttpResponse
objects will be new for each new request, and the thread, but not the servlet instance.
How do servlets work? Instantiation, sessions, shared variables and multithreading
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
and all of included web-fragment.xml
files is parsed, and each <servlet>
, <filter>
and <listener>
found (or each class annotated with @WebServlet
, @WebFilter
and @WebListener
respectively) will be instantiated once and be kept in the server's memory as well, registred via the ServletContext
. For each instantiated filter, its init()
method is invoked with a new FilterConfig
argument which in turn contains the involved ServletContext
.
When a Servlet
has a <servlet><load-on-startup>
or @WebServlet(loadOnStartup)
value greater than 0
, then its init()
method is also invoked during startup with a new ServletConfig
argument which in turn contains the involved ServletContext
. Those servlets are initialized in the same order specified by that value (1
is 1st, 2
is 2nd, etc). If the same value is specified for more than one servlet, then each of those servlets is loaded in the same order as they appear in the web.xml
, web-fragment.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.
When the servlet container is finished with all of the above described initialization steps, then the ServletContextListener#contextInitialized()
will be invoked with a ServletContextEvent
argument which in turn contains the involved ServletContext
. This will allow the developer the opportunity to programmatically register yet another Servlet
, Filter
or Listener
.
When the servlet container shuts down, it unloads all web applications, invokes the destroy()
method of all its initialized servlets and filters, and all Servlet
, Filter
and Listener
instances registered via the ServletContext
are trashed. Finally the ServletContextListener#contextDestroyed()
will be invoked and the ServletContext
itself will be trashed.
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 (e.g. user with a web browser, or programmatically using URLConnection
) sends an HTTP request, the servlet container creates new HttpServletRequest
and HttpServletResponse
objects and passes them through any defined Filter
in the chain and, eventually, the Servlet
instance.
In the case of filters, the doFilter()
method is invoked. When the servlet container's 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 URL, headers, query string 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 available 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 stores 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 any decent web browser and web server must 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 been idle (i.e. not used in a request) 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 being 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
andHttpServletResponse
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
andListener
instances live as long as the web app lives. They are shared among all requests in all sessions. - Any
attribute
that is defined inServletContext
,HttpServletRequest
andHttpSession
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 anattribute
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 about 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 {
Object thisIsThreadSafe;
thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}
See also:
- What is the difference between JSF, Servlet and JSP?
- Best option for Session management in Java
- Difference between / and /* in servlet mapping url pattern
- doGet and doPost in Servlets
- Servlet seems to handle multiple concurrent browser requests synchronously
- Why Servlets are not thread Safe?
How can only one servlet instance exist in multiple threads?
Servlets are instantiated by the Servlet container, you have no explicit control over that.
You can't force to have only 1 instance of a specific Servlet in a Servlet container (although typically this will be the case); at least not in an official way because the Servlet API does not provide any way to do it. Some container implementations might provide you an arbitrary API for this, but I wouldn't count on it.
Instead make your Servlet implementation so that it doesn't depend on how many instances there are (e.g. do not use static attributes).
Update:
Quoting from the Servlet specification 2.3 section #2.3.1:
It is important to note that there can be more than one instance of a
given Servlet class in the servlet container. For example, this can
occur where there was more than one servlet definition that utilized a
specific servlet class with different initialization parameters. This
can also occur when a servlet implements the SingleThreadModel
interface and the container creates a pool of servlet instances to
use.
Related Topics
How to Count Frequency of Characters in a String
How to Define a Relative Path in Java
How to Create and Handle Composite Primary Key in JPA
Why Does the Jvm Still Not Support Tail-Call Optimization
Is There a Good Natural Language Processing Library
Read Properties File Outside Jar File
Rationale for Matcher Throwing Illegalstateexception When No 'Matching' Method Is Called
How to Split a Java String at Backslash
Simple Division in Java - Is This a Bug or a Feature
Convert String Date to String Date Different Format
Regular Expression with Variable Number of Groups
How Can Non-Ascii Characters Be Removed from a String
How to Read PDF Files Using Java
What Is the List of Valid @Suppresswarnings Warning Names in Java