Using ThreadLocal in instance variables

PNS picture PNS · Mar 11, 2012 · Viewed 9.4k times · Source

Do Java ThreadLocal variables produce thread-local values if they are used as instance variables (e.g., in a method that generates thread-local objects), or must they always be static to do so?

As an example, assume a typical scenario where several, expensive to initialize objects of a class that is not thread-safe, need to be instantiated in a single static initialization block, stored in static variables of a single class (e.g., in a Map data structure) and from then on used for intensive processing by numerous different threads.

To achieve thread safety, obviously a different copy of each static object must be passed. For instance, Java DateFormat objects that need to be safely used across different threads.

In many examples one can find on the web, the approach seems to be declaring separately each ThreadLocal variable, instantiate the new object in the initialValue() method and then use the get() method to retrieve a thread-local instance.

This approach is not very efficient if there are dozens or hundreds of such objects to be created, each with its own initialization parameters. For example, many SimpleDateFormat objects with a different date pattern each.

If the instantiation of the objects could be done in a loop which produces a different value in each iteration, a generic method for producing the thread-local instances would be needed, after each value is created by properly initializing the corresponding object.

Based on the above, the following generic static method would not work, because the same reference is produced on every call to initialValue():

// Each value is an object initialized prior to calling getLocal(...)
public static final <T> T getLocal(final T value)
{
    ThreadLocal<T> local = new ThreadLocal<T>()
    {
        @Override
        protected T initialValue()
        {
            return value;
        }
    };

    return local.get();
}

Instead, a mechanism for creating a new object inside initialValue() is needed. So, the only generic approach is probably using reflection, in a pattern similar to

private static final <T> T getLocal(
        final Constructor<T> constructor, final Object[] initargs)
{
    ThreadLocal<T> local = new ThreadLocal<T>()
    {           
        @Override
        protected T initialValue()
        {
            T value = null;

            try // Null if the value object cannot be created
            {
                value = constructor.newInstance(initargs);
            }
            catch (Exception e)
            {
            }

            return value;
        }
    };

    return local.get();
}

Then, of course, there is the type-specific option, where one could just use the ThreadLocal pattern in the loop for declaring each variable.

For example, in the case of DateFormat, in a single, static initialization block, one could do

private static String[] patterns = ... // Get date patterns
private static DateFormat format;

public static Map<String, DateFormat> formats = new HashMap<String, DateFormat>();

static
{
    for (final String pattern:patterns)
    {
        format = new ThreadLocal<DateFormat>()
        {           
                @Override
            protected DateFormat initialValue()
                {
            return new SimpleDateFormat(pattern);
            }
        }.get();

        formats.put(pattern, format);
}

From then on, the formats map will be read by different classes, across different threads, each time in order to invoke the format() or parse() method of one or more DateFormat objects stored in the map.

Does any of the above approaches make sense for the case described, or should the ThreadLocal declarations be static?

Answer

Tom Anderson picture Tom Anderson · Mar 11, 2012

To answer your headline question, ThreadLocal provides each thread with a separate value of that ThreadLocal instance. So if you have two instances in different places, each thread will have separate values in each. This is why ThreadLocals are so often static; if all you want is a separate value for a variable per thread, then you only need one ThreadLocal for that variable in the JVM.

A.H.'s answer is very good, and i will suggest a further variation on it. It looks like you might want to put the control over the date formats in the calling code, not in the definition of the map. You could do that with code something like:

public class DateFormatSupplier {
    private static final Map<String, ThreadLocal<DateFormat>> localFormatsByPattern = new HashMap<String, ThreadLocal<DateFormat>>();

    public static DateFormat getFormat(final String pattern) {
        ThreadLocal<DateFormat> localFormat;
        synchronized (localFormatsByPattern) {
            localFormat = localFormatsByPattern.get(pattern);
            if (localFormat == null) {
                localFormat = new ThreadLocal<DateFormat>() {
                    @Override
                    protected DateFormat initialValue() {
                        return new SimpleDateFormat(pattern);
                    }
                };
                localFormatsByPattern.put(pattern, localFormat);
            }
        }
        return localFormat.get();
    }
}

Where you create the ThreadLocals lazily.