Session synchronization issues
It's quite feasible that different threads will access the same HttpSession object.
This can happen simultaneously (a client makes two simultaneous connections with
the same session ID). Or even with serial requests from a client, it's quite likely that
a different thread will service each call (since servers generally hand out requests arbitrarily to
the next available thread from a "pool" of threads). So in either case, we need to be sure
that the different threads see a consistent view of a given HttpSession object.
(Remember: synchronization isn't just about the problem of concurrent access;
it's also about the problem of data visibility between different threads. You may
wish to see the section on variable
synchronization for more information.)
Calls to getAttribute() and setAttribute() should have the same
synchronization semantics as accessing a synchronized hash map. This means
that:
- concurrent calls to getAttribute() and setAttribute()
are thread-safe in the sense that they will not leave the
internal data structure in a "broken" state...
- ...unless your Servlet runner has a synchronization bug, of course;
- if you want to combine multiple sets/gets into an atomic operation,
then you need explicit synchronization.
The last point is the one that people seem to either forget or not appreciate. Let's say you
want to associate with a session a user ID plus some other properties that are dependent on that
user ID: user name, real name etc. If we set these as individual properties on the session,
then we need synchronization:
HttpSession sess = req.getSession(true);
synchronized (sess) {
sess.setAttribute("USERID", id);
sess.setAttribute("USERNAME", username);
...
}
Without the synchronization, there is a risk that another thread could read, say, the
user ID but a null user name.
As of Java 5, an alternative, often preferable, strategy is to use a mutable object to combine
the properties. Remember that an immutable object
is one with final fields. So if we wrap our user data inside a DBUser object
defined with final fields as follows1:
public class DBUser {
private final int id;
private final String username;
private final String firstName;
...
public static DBUser retrieve(Stirng username) {
... retrieve data from DB ...
return new DBUser(id, ...);
}
private DBUser(int id, ...) {
// set values on all the final fields
this.id = id;
...
}
}
Then as of Java 5, it is completely safe to do the following, without synchronization:
DBUser user = DBUser.retrieve(username);
HttpSession sess = req.getSession(true);
sess.setAttribute("USER", user);
A special requirement of the JVM is that, if the object is visible at all to another thread,
then the values of all its final fields will be visible. So there is no chance of the other
thread "seeing" the user object in an inconsistent state, as would be the case if all of the
fields were set (without synchronization) as individual attributes on the HttpSession object.
1. Incidentally, the reason for the static retrieve() method
is just a design decision. So long as we set values on all the final fields at some point in the
constructor, it doesn't matter at precisely what point, or whether we do something else (such as
connecting to a database) beforehand. I consider it good design not to connect to
a database inside the constructor of a "wrapper" object, but that is ultimately personal design preference.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.