Know the JVM Series – 4 – Thread Locals

Thread Local Storage (TLS) is a special construct in multi-threaded programming, which allows to associate a variable with a particular thread. This is different from normal variables, which are associated with a process. In other words, a normal variable will be shared by threads of a same process (in procedural programming terms). However, a Thread Local variable has a copy of it per thread, and modifications by a particular thread is applicable for code running inside that thread only.

With the inherent multi-threaded nature of most enterprise Java applications, having some understanding about Thread Local variables are useful to solve some problems which would otherwise be difficult. We’ll start our discussion by looking into a practical problem that is faced by JEE applications, so that we have a good understanding about the power Thread Locals.

Consider a web based JEE application, which consists of JSP/Servlet based presentation tier, EJB based service facade tier, and business logic implemented in POJOs, and finally Hibernate in the persistence tier. In this application, users are required to authenticate before using secured parts of the application, and we will be using the standard JAAS to facilitate this. Assume that in our POJO based Business Logic classes and Hibernate DAOs, we need to obtain the currently logged in users ID for auditing purposes. How can we solve this problem?

The traditional solution would be to rely on JAAS based EJB methods to obtain the logged in user details, by calling EJB Session Context’s sessionContext.getCallerPrincipal().getName() method, with in the EJB session beans (or in an interceptor). Once we have obtained these with in our Session Facade, then we can pass in the current user’s ID as a parameter to our business logic POJO and DAO methods. This requires us to write methods such as following in our BL / DAO classes.

public void saveBill(Bill bill, String currentUserID);
public void removeBill(Bill bill, String currentUserID);
public void applyDeductions(Bill, String currentUserID);

Since we have to pass in the user ID to each and every method that we need to audit, our methods become verbose. With a little bit of understanding about how JEE applications work behind the scenes, and some knowledge of ThreadLocals, we can solve this problem, and have much cleaner methods which does not need to take in extra parameters as above.

We all know that in JEE applications, each request from the user is associated with a thread. Therefore, when an EJB invocation occurs, we will have each request being serviced by one particular thread, starting from our session bean (we will discard the presentation tier in this example). All subsequent BL class invocations, and DAO invocations will be done with in that particular thread. So if we could store the authentication details with in the thread, and access that from any place else where we need that information, we don’t have to pass those information as method arguments. Case Solved! (we will look at a full implementation of this in the latter part of this article). Such is the power of ThreadLocals, and due this reason, most libraries out there including Hibernate, JUnit, Spring, etc leverage on this for various functionalities.

Now that we have looked at one scenario where Thread Locals could come into rescue, let’s look into some basics of using Thread Locals.

Java supports thread local storage through the java.lang.ThreadLocal class. This is a generic class which could be used to store thread local variables. Following is a simple example of a ThreadLocal being used to store an Integer with in a thread.

package test.threadlocal;

/**
 * Simple Thread Local Example.
 *
 * @author Yohan Liyanage
 *
 */
public class SimpleThreadLocal {

    // This is the Thread Local Storage.
    // Each Thread will have its own copy of
    // the integer saved in this thread local.
    private static ThreadLocal threadLocal
                            = new ThreadLocal();

    /**
     * This is just a simple runnable class, which generates a random number,
     * prints it to sysout, stores it in the ThreadLocal, and shows it back
     * after a while (to let other threads run).
     *
     * @author Yohan Liyanage
     */
    private static class SimpleThreadLocalRunnable implements Runnable {

        @Override
        public void run() {

            // Generate a Random Number for example purposes
            int rnd = (int) (Math.random() * 100);

            System.out.println("Thread "
                + Thread.currentThread().getName() + " setting int : " + rnd);

            // Save it in the TLS
            SimpleThreadLocal.threadLocal.set(rnd);

            try {
                // Let other threads run
                // so that we can see that each threads has its own copy
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Print the current value of the TLS
            System.out.println("Thread " + Thread.currentThread().getName()
                + " retrieving int : " + SimpleThreadLocal.threadLocal.get());

        }
    }

    /**
     * Start 5 threads to do the above.
     *
     * @param args
     */
    public static void main(String[] args) {

        for (int i=0; i < 5; i++) {
            new Thread(new SimpleThreadLocalRunnable()).start();
        }

    }

}

And the output is,

Thread Thread-0 setting int : 28
Thread Thread-1 setting int : 57
Thread Thread-2 setting int : 67
Thread Thread-3 setting int : 92
Thread Thread-4 setting int : 54
Thread Thread-0 retrieving int : 28
Thread Thread-2 retrieving int : 67
Thread Thread-3 retrieving int : 92
Thread Thread-4 retrieving int : 54
Thread Thread-1 retrieving int : 57

The set() method of the ThreadLocal class allows a thread to store a variable in the thread local storage. Subsequent get() calls from the same thread will return the value set by that thread previously. In the mean time, if another thread calls the get() method (without setting any value before), it will simply get null, since that threads local storage does not contain any value.

Since we are now aware about the basics of using a Thread Local, let’s dig deeper into the details of the Thread Local class. We have already covered the set() and get() methods of the class. In addition to this, there are two other methods, namely remove() and initialValue(). As the name suggests, the purpose of initialValue() method is to allow developers to provide a specific value as the default. The use of initial value is as below :

package test.threadlocal;

/**
 * Thread Local Initial Value Example.
 *
 * @author Yohan Liyanage
 *
 */
public class ThreadLocalInitialValueDemo {

    private static ThreadLocal threadLocal = new ThreadLocal() {

        @Override
        protected Integer initialValue() {
            return Integer.valueOf(5);
        }

    };

    public static void main(String[] args) {

        System.out.println("Calling thread local without setting : " + threadLocal.get());

    }
}

Output,

Calling thread local without setting : 5

Hence, the initialValue method could be overridden to provide altnerate default values, instead of returning null. In fact, if you look into the source of this class, you would see that the implementation of this method in java.lang.ThreadLocal is to return null. So whenever a call to get() method is done, a check is done to see whether a value already exists, and if not, this intialValue method is invoked, and the result from this method is stored as the value of the ThreadLocal for that particular thread. Hence, this method runs only once (with one exception mentioned later on), and that value will be stored in the ThreadLocal storage in the first get() invocation. This is demonstrated below.

package test.threadlocal;

/**
 * Thread Local Initial Value Example.
 *
 * @author Yohan Liyanage
 *
 */
public class ThreadLocalInitialValueDemo2 {

    private static ThreadLocal threadLocal = new ThreadLocal() {

        @Override
        protected Integer initialValue() {

            System.out.println("Returning Initial Value...");

            return Integer.valueOf(5);
        }

    };

    public static void main(String[] args) {

        System.out.println("Calling thread local without setting... ");
        System.out.println("Value Returned :  " + threadLocal.get() + "nn");
        System.out.println("Calling thread local without setting again ! ... ");
        System.out.println("Value Returned :  " + threadLocal.get() + "nn");

    }

}

and what we will get in the console is,

Calling thread local without setting...
Returning Initial Value...
Value Returned :  5

Calling thread local without setting again ! ...
Value Returned :  5

The remove method allows to clear off the values stored in the ThreadLocal variable by the same thread. This is useful for clean up operations. One thing to keep in mind when using thread locals is that if we don’t clear off the values stored in the thread local variable, it is possible to lead up to memory leaks due to content allocated in thread specific storage. Specially in situations where ThreadPools are used, it is important to remove the content before releasing a thread back to the pool. Otherwise, the thread which is returned back to the pool will hold back a reference to whatever the values stored in the Thread Local, preventing it from being garbage collected. However, when a Thread dies (completion / error), the content in the Thread Local variables will be dereferenced, and thus will be eligible for GC if no other reference exists.

If you invoke get() after invoking remove() with in the same thread, then the initialValue() method will be invoked again (that is, if you haven’t set any value after removing), and the default value will be returned. Therefore, initialValue() method will be executed multiple times with in the same thread if get() is used after remove().

package test.threadlocal;

/**
 * Thread Local Initial Value + Removals Example.
 *
 * @author Yohan Liyanage
 *
 */
public class ThreadLocalRemoveInitialValue {

    private static ThreadLocal threadLocal = new ThreadLocal() {

        @Override
        protected Integer initialValue() {

            System.out.println("Returning Initial Value...");

            return Integer.valueOf(5);
        }

    };

    public static void main(String[] args) {

        System.out.println("Calling thread local without setting... ");
        System.out.println("Value Returned :  " + threadLocal.get() + "nn");

        System.out.println("Removing... " + "nn");
        threadLocal.remove();

        System.out.println("Calling thread local without setting, after remove ... ");
        System.out.println("Value Returned :  " + threadLocal.get() + "nn");

    }

}

which results in,

Calling thread local without setting...
Returning Initial Value...
Value Returned :  5

Removing... 

Calling thread local without setting, after remove ...
Returning Initial Value...
Value Returned :  5

Let’s look at a real life example of Thread Local variables. This is the full implementation of the previously described scenario of storing authenticated user details in a ThreadLocal so that such information can be accessed from POJOs. As mentioned before, keep in mind that this is a EJB based application (EJBs are service facades which delegates to the POJOs). Therefore, we will be using some EJB API content for this.

In order to store the authenticated user details, we have the following class. This is merely a wrapper around the ThreadLocal class.

package com.yohanliyanage.tls.demo.service.platform.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Holds Authentication Context Information for a given thread via a ThreadLocal variable.
 *
 * @author Yohan Liyanage
 * @version 1.0
 * @since 1.0
 */
public final class AuthenticationContext {

    private static final Log LOG = LogFactory.getLog(AuthenticationContext.class);
    private static ThreadLocal authContext = new ThreadLocal();

    /**
     * No external instantiation.
     */
    private AuthenticationContext() {
        // No external instantiation
    }

    /**
     * Returns Logged In User's ID.
     * @return logged in users ID if exists, or null.
     */
    public static String getAuthenticatedUserID() {

        String authID = authContext.get();

        if (authID==null) {
            // Log a WARN level message if this method fails to return a User ID
            LOG.warn("getAuthenticatedUserID() : No Authenticated User ID available in current thread context.");
        }

        return authID;
    }

    /**
     * Checks whether the Authentication Context data is available.
     *
     * @return true if the authentication context data is available.
     */
    public static boolean isContextAvailable() {
        return authContext.get()!=null;
    }

    /**
     * Sets the Authenticated User ID. Note that this method is package-private,
     * hence only same package classes can access it.
     * @param userID user's ID
     */
    static void setAuthenticatedUserID(String userID) {
        authContext.set(userID);
    }

    static void clearAuthenticatedUserID() {
       authContext.remove();
    }

}

Then, we need to have a mechanism of populating the authenticated user details for each request. We can either do it in each and every method of the EJB Session Bean, or else, we could rely on an interceptor to do the same, which is much better. The EJB interceptor code is as follows.

package com.yohanliyanage.tls.demo.service.platform.security;

import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * EJB Interceptor which saves JAAS authentication
 * related information in a Thread Local variable.
 *

 * This allows authenticated user details to be accessed in any place with in the thread of execution.
 *
 * @author Yohan Liyanage
 * @version 1.0
 * @since 1.0
 */
public class AuthenticationContextInterceptor {

    private static final Log LOG = LogFactory.getLog(AuthenticationContextInterceptor.class);

    @Resource
    private SessionContext sessionContext;

    public AuthenticationContextInterceptor() {
        super();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Constructing an instance of AuthenticationContextInterceptor...");
        }
    }

    /**
     * Intercept EJB invocation and populate the authentication context with User ID.
     *
     * @param inv
     * @return
     * @throws Exception
     */
    @AroundInvoke
    public Object intercept(InvocationContext inv) throws Exception {

        if (LOG.isDebugEnabled()) {
            LOG.debug("AuthenticationContextInterceptor Intercepting EJB Invocation...");
        }

        try {
            if (sessionContext.getCallerPrincipal() != null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Setting the authenticated user details in Authentication Context: "
                            + sessionContext.getCallerPrincipal().getName());
                }
                AuthenticationContext.setAuthenticatedUserID(sessionContext.getCallerPrincipal().getName());
            }
        } catch (IllegalStateException e) {

            if (LOG.isDebugEnabled()) {
                LOG.debug("Authentication details not available. Unable to set Authentication Context.");
            }
        }
        try {
            return inv.proceed();
        }
        finally {
              AuthenticationContext.clearAuthenticatedUserID();
        }
    }
}

Note that we are intecepting all calls to our session bean, and we push in the authenticated user details to ThreadLocal storage before invoking the method. Since each method runs in its own thread, our authentication details will be available for the POJOs. Note that this will not work for cases where new threads are spawned. But in most cases, this is applicable.

Given that the interceptor is configured properly (either using annotations or XML), the authentication context details can be accessed from our POJOs as follows (note that the request should come through the EJB).

AuthenticationContext.getAuthenticatedUserID();

This is one example use case of ThreadLocals, and there are many more depending on the problems that you will face. For example, if you look at the source of the TransactionSynchronizationManager (which is inturn used by OpenSessionInView Filter / Interceptor) of Spring Framework, it heavily relies on ThreadLocals to get things done. Most of the Java Authentication and Authorization API (JAAS) implementations use ThreadLocals to store authenticated user details. Hibernate uses thread locals to bind sessions to the current thread. So it is upto you to decide whether using thread locals to solve a particular problem. But with the understanding of constructs such as thread locals, you will have more options to solve the same problem, in much better ways.


UPDATE (15th Nov 2010)

  • Added missing example for the Integer generation.
  • Fixed the missing try-finally block in AuthenticationContextInterceptor. Thanks to @fluminis for pointing out.

3 comments

  1. Nice article.
    But, I think your class AuthenticationContext causes a memory leak: you never clean the ThreadLocal variable.
    If you don’t call explicitly the remove() method at the end of your thread to clean the ThreadLocal, you have an instance of the variable authID in the heap.

    1. Hi,

      I took the code from one project that I’ve done, and I was going to add the try-finally block to invoke the remove operation. But forgot that :).

      In that application, this does not lead to a memory leak because it is deployed to a size restricted thread pool (which is the case most of the time). The thread pool is configured to have at most 500 threads. So in the worst case scenario, we end up with 500 additional strings, and Java strings are anyway stored in the String Constant Pool. When a thread is re-used, the value of the ThreadLocal will be updated.

      But yes, I should have included the try-finally block, and if the value I store is something large, this could have caused issues.

      Thanks for pointing out. I will update the article.

      Regards,
      Yohan.

Leave a Reply

Your email address will not be published. Required fields are marked *