MultithreadingS2C Home « Multithreading
In the last lesson we looked at the three scopes we can use with our servlets, these being the request, session and context scopes. What we didn't talk about were situations where multiple threads access resources within a scope, generally attributes, and how this affects the values held therein. In this lesson we discuss multithreading in web applications, whether a scope is thread safe, and if not, when and how we can protect resources within that scope from concurrent access.
The main drive of this lesson will be to discuss thread safety with regards to attributes and the various scopes they reside in. Before we do this we will discuss the other resources available in Java as the rules for these resources apply regardless of scope.
Class Variables
Class variables are not thread safe because all threads share the same method area, and the method area is where class variables are stored. This means that multiple threads can attempt to use the same class variables concurrently and so thread safety cannot be guaranteed.
Instance Variables
Instance variables are not thread safe because all threads share the same heap, and the heap is where instance variables are stored. This means that multiple threads can attempt to use the same instance variables concurrently and so thread safety cannot be guaranteed. There is also no certainty that a client accessing the same URL will even get the same instance variables. It's generally better to use session attributes in place of instance variables, but if for whatever reason you really have to use an instance variable then you will need to add synchronization to guarantee thread safety, which is of course a hit to performance.
Local Variables
Local variables which consist of method parameters, return values and variables declared within methods reside on the stack and the JVM allocates each thread its own stack. Therefore a thread only has access to its own stack and knows nothing about local variables belonging to another thread. What this means is that we don't have to worry about thread safety with regards to local variables as they are inherently thread safe by design.
Request AttributesTop
In the Request & Response - One Thread Per Request section we looked at how the container creates a new java thread every time a servlet request is
received. The thread will have its own stack for local variables as discussed above. These will include the method parameters to the service()
method (ServletRequest
and ServletResponse
objects), the method parameters to any invoked doXXX()
method (HttpServletRequest
and HttpServletResponse
objects), as well
as any attributes attached to the ServletRequest
or HttpServletRequest
object. What this means is that any request attributes we create whether they are in the service()
method, or an invoked
doXXX()
method, can only ever be accessed by this thread and are therefore thread safe.
The following slideshow shows three client requests accessing a servlet and the resources that can be accessed. Press the button below to step through the slideshow:
Client 1 has clicked on a link that points to a resource that is a servlet rather than a static HTML page, so the web server directs the HTTP request to our web container
and creates a new thread (Thread1) for this request. The service()
method, any invoked doXXX()
method, the request object and any request attributes reside in their own stack allocated by the JVM.
We use the ServletRequest
type when working with request attributes. The following code snippet shows request attributes being used:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Get request attributes
String getAttr1 = (String) req.getAttribute("name"));
String getAttr2 = (String) req.getAttribute("age"));
// Get all request attributes
Enumeration<String> getRequestAttributes = req.getAttributeNames();
// Set request attributes
req.setAttribute("name", "charlie");
req.setAttribute("age", "22");
// Remove request attributes
req.removeAttribute("name");
req.removeAttribute("age");
}
Session AttributesTop
In the Attributes & Scope - Session Scope section we learnt that if we require sessions, then the container uses the HttpSession
interface to create
a session object to use between an HTTP client and an HTTP server. This is achieved using the overloaded getSession()
method of the HttpSession
interface and the container keeps
track of session objects using a unique session identifier that identifies each individual session.
You would think then that using this unique session identifier and even if multiple servlets are invoked, a client can only have one request running at a time and as we now know request attributes are thread safe and run in their own stack. This would seem to infer that session attributes are also thread safe as only one request is being processed for a client at any given time. Sadly this is not the case as we can have a scenario where more than one request comes in from the client. How is this possible? Well the client can just open another browser window and invoke the servlet where the session was created. This could happen for any number of reasons, including a slow response to the initial request and the user gets fed up with waiting, we have all done this at some point. The request attributes are still thread safe because they are running in their own stacks but the servlet will see we have an exisiting session and use this. This could mean an overlap of retrieving and updating session attributes, via the two requests, making session attributes inherently unsafe.
The following slideshow shows such a scenario. Press the button below to step through the slideshow:
The service()
method invokes one of the doXXX()
methods. Within the doXXX()
method we create our session object using the
getSession()
method and create some session attributes.
So how can we protect our session attributes from the above scenario? Well just like with any concurrent processing where we need to ensure only one thread has access to a resource at any given time we
can use synchronization. To protect our session attributes we need to synchronize on the HttpSession
object and do our setAttribute()
method updates there. The following code
snippet shows session attributes being updated within a synchronization block:
HttpSession session = HttpServletRequest.getSession();
synchronized(session) {
session.setAttribute("name", "charlie");
session.setAttribute("age", "22");
}
We would also clearly need to synchronize when removing attributes using the removeAttribute()
method. The following code snippet shows session attributes being removed within a synchronization block:
HttpSession session = HttpServletRequest.getSession();
synchronized(session) {
session.removeAttribute("name");
session.removeAttribute("age");
}
If it's important that users don't get session attributes during updates then our getAttribute()
and getAttributeNames()
methods should also be synchronized. The following
code snippet shows session attributes being output to a console and stored in an Enumeration
from within a synchronization block:
HttpSession session = HttpServletRequest.getSession();
synchronized(session) {
System.out.println(session.getAttribute("name"));
System.out.println(session.getAttribute("age"));
// Get all session attributes
Enumeration<String> getSessionAttributes = session.getAttributeNames();
}
Context AttributesTop
Context attributes are application wide and so can be used by any servlet in the web application. This also means they are the most likely to be affected by concurrent update as potentially any request from a client to the web application could involve an update to a context attribute.
The following slideshow shows context attributes being set within two applications. Whenever any of the servlets access the same context attributes there is a possibility of concurrent access. Press the button below to step through the slideshow:
Client 1 requests ServletA
which now has access to the context object of web app 1 and updates context attributes.
So how can we protect our context attribues from the above scenario? It really depends on what you are using the context attributes for. If you intend these attributes as read only then you can put
setAttribute()
method updates within the init()
servlet lifecycle method. You would then have to ensure your programmers were aware that the setAttribute()
method
update is not to be used within the service()
and any doXXX()
methods. So this would have to be enforced as a programming standard and lets face it you're now hardcoding read
only attributes and enforcing standards when you could add these declaratively in the DD using the <context-param> top-level
element in conjunction with its <param-name> and <param-value>
sub-level elements. So maybe not the most elegant solution.
The alternative just like with any concurrent processing where we need to ensure only one thread has access to a resource at any give time and what we do with session attributes is to use synchronization.
Unlike the HttpSession
object we don't create our have direct access to the ServletContext
object, so what do we synchronize on? To protect our context attributes we need to
synchronize on the getServletContext()
method and do our setAttribute()
method updates there. The following code snippet shows context attributes being updated within a synchronization block:
synchronized(getServletContext()) {
getServletContext().setAttribute("name", "charlie");
getServletContext().setAttribute("age", "22");
}
We would also clearly need to synchronize when removing attributes using the removeAttribute()
method. The following code snippet shows context attributes being removed within a synchronization block:
synchronized(getServletContext()) {
getServletContext().removeAttribute("name");
getServletContext().removeAttribute("age");
}
If it's important that users don't get context attributes during updates then our getAttribute()
and getAttributeNames()
methods should also be synchronized. The following
code snippet shows context attributes being output to a console and stored in an Enumeration
from within a synchronization block:
synchronized(getServletContext()) {
System.out.println(getServletContext().getAttribute("name"));
System.out.println(getServletContext().getAttribute("age"));
// Get all servletContext attributes
Enumeration<String> getContextAttributes = getServletContext().getAttributeNames();
}
Obviously with context attributes you have to be very careful with synchronization as these attributes are application wide and synchronization is a hit to performance. Also if context attributes are in constant need of updating they can cause bottlenecks within a web application and seriously degrade performance. So in these situations it might be better to look at your overall design and place these attributes elsewhere.
Lesson 6 Complete
In this lesson we looked at multithreading and how attributes in the different scopes are affected.
What's Next?
In the next lesson we look at redirects & request dispatching.