Most commercial Java developers nowadays are working on projects that involve the JEE environment. As such they work on code that runs within the context of the servlet container or the application server. Which provides many services for the application developer, including session management. But what do you do if you suddenly find yourself outside the JEE container again and for some reason still have to maintain session data? If your needs can be met with request-scoped session management, ThreadLocal allows you to create your own session data repository with a minimum of fuss.

On a recent project, I found myself working on a service library that I knew would be running within a session-based environment (as part of a JEE application in fact). For reasons not relevant here, it had become necessary to handle request-scoped session data within the library. However, as a library, my code would not have direct access to the session provided by the container. It seemed therefore that I would have to roll my own session data management.

While working on this problem I came across one of the most undervalued and hidden tools that the Java platform offers for exactly this situation: the java.lang.ThreadLocal class. This class allows you to associate an object with a thread — essentially, it’s a hashtable that uses thread objects as keys. Which means that if you put a static instance of ThreadLocal somewhere, you have added a thread-scoped global variable to your application.

The few people on the Internet that mention the existence of ThreadLocal all seem to feel that ThreadLocal is a very dangerous beast to handle and warn against using it unless you really understand what you are doing. In my experience however ThreadLocal is not very hard to handle at all once you know how it works.

As explained above, ThreadLocal is essentially a specialized hashtable that uses thread objects (instances of java.lang.Thread) as a key. That means that ThreadLocal allows you to associate one single object with any given thread. To store many different values per thread you must, therefore, either use a separate ThreadLocal instance per value or store a wrapper object in a single ThreadLocal instance that can hold all the session data you need.

To store and retrieve this one single object per thread, ThreadLocal has the following methods:

  • get(): retrieves the object stored for the current thread (returns null if there is none)
  • set(T value): stores an object of type T for the current thread
  • remove(): removes whatever object was stored for the current thread.
  • initialValue(): returns the initial value of the object stored for the current thread; only useful if you subclass ThreadLocal yourself.

Note that, even though ThreadLocal is a hashtable, you don’t have to pass in a key anywhere. Since the key is always the executing thread, ThreadLocal does all that for you. ThreadLocal also handles cleanup after the thread dies, so you don’t have to clear data from any ThreadLocal instances yourself once a thread dies.

Also note that the desciption given above is for the Java 5.0+ version of ThreadLocal, which has been outfitted with a type parameter. Previous versions of ThreadLocal just use java.lang.Object as the type of the object being stored and retrieved.

Once you’ve got the above straight, actually using ThreadLocal is very straightforward. You need the type of whatever you want to store in your ThreadLocal and you need an instance of ThreadLocal that can exist across threads — that’s a static variable to you and me. So, say for example that the type you want to store in your ThreadLocal is called MySessionData. The simplest way to use ThreadLocal for this is to write a utility class like so:

public class SessionManager
{

private static final ThreadLocal threadLocal;

static
{

threadLocal = new ThreadLocal();

}

public static synchronized MySessionData getSessionData()
{

return threadLocal.get();

}

}

This class both manages the ThreadLocal instance that will exist across threads and manages access to it in a friendly way that hides the details of ThreadLocal from the rest of your application.

Note that we have not done anything in the above code about creating and storing instances of MySessionData. For this, there are two options. First, you could create a setSessionData() method in the SessionManager given above, which provides access to the ThreadLocal’s set method. Alternatively, you can subclass ThreadLocal like so:

public class MyOwnThreadLocal extends ThreadLocal
{

@Override

protected MySessionData initialValue()
{

return new MySessionData();

}

}

In this case the code for the session manager becomes:

public class SessionManager
{

private static final ThreadLocal threadLocal;

static
{

threadLocal = new MyOwnThreadLocal();

}

public static synchronized MySessionData getSessionData()
{

return threadLocal.get();

}

}

Note that there is no need for an explicit call to ThreadLocal’s initialValue() method anywhere (in fact this would be impossible, since the method is protected); the get() method calls initialValue() if there is no object stored in the ThreadLocal instancefor the executing thread.

One thought on “Managing “session” data with ThreadLocal

  • March 22, 2008 at 10:09 am
    Permalink

    Although ThreadLocal can be very handy and useful at times, there is a very big caveat in its use.

    When using ThreadLocal in combination with thread pooling, extra caution is required. Typicaly, a thread will not be destroyed when it has finished running, but will be put back in the pool, waiting for a new task. It is good practice to clean after yourself when you’ve finished something. Java developers have been spoiled with the garbage collector, but that one doesn’t help you with ThreadLocals, unless the Thread it is local to is destroyed.

    So, make sure you remove settings in your ThreadLocal to prevent getting data from another request that happened to run on the same thread.

    Oh, and one more little thing. There is no point in making access to the ThreadLocal variable synchronized (as done in the last example), since each thread will access its own copy of the threadlocal data.

Comments are closed.