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:

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.