Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is Java's ThreadLocal implemented under the hood?

How is ThreadLocal implemented? Is it implemented in Java (using some concurrent map from ThreadID to object), or does it use some JVM hook to do it more efficiently?

like image 232
ripper234 Avatar asked Jul 29 '09 19:07

ripper234


People also ask

How is ThreadLocal implemented?

The implementation of ThreadLocalMap is not a WeakHashMap , but it follows the same basic contract, including holding its keys by weak reference. Essentially, use a map in this Thread to hold all our ThreadLocal objects.

What is ThreadLocal class how and why you should use it?

The ThreadLocal class is used to create thread local variables which can only be read and written by the same thread. For example, if two threads are accessing code having reference to same threadLocal variable then each thread will not see any modification to threadLocal variable done by other thread.

Where is ThreadLocal used?

ThreadLocal is useful, when you want to have some state that should not be shared amongst different threads, but it should be accessible from each thread during its whole lifetime. As an example, imagine a web application, where each request is served by a different thread.

Why ThreadLocal is static and final?

The reason is that the variables are accessed via a pointer associated with the thread. They act like global variables with thread scope, hence static is the closest fit. This is the way that you get thread local state in things like pthreads so this might just be an accident of history and implementation.


1 Answers

All of the answers here are correct, but a little disappointing as they somewhat gloss over how clever ThreadLocal's implementation is. I was just looking at the source code for ThreadLocal and was pleasantly impressed by how it's implemented.

The Naive Implementation

If I asked you to implement a ThreadLocal<T> class given the API described in the javadoc, what would you do? An initial implementation would likely be a ConcurrentHashMap<Thread,T> using Thread.currentThread() as its key. This will would work reasonably well but does have some disadvantages.

  • Thread contention - ConcurrentHashMap is a pretty smart class, but it ultimately still has to deal with preventing multiple threads from mucking with it in any way, and if different threads hit it regularly, there will be slowdowns.
  • Permanently keeps a pointer to both the Thread and the object, even after the Thread has finished and could be GC'ed.

The GC-friendly Implementation

Ok try again, lets deal with the garbage collection issue by using weak references. Dealing with WeakReferences can be confusing, but it should be sufficient to use a map built like so:

 Collections.synchronizedMap(new WeakHashMap<Thread, T>()) 

Or if we're using Guava (and we should be!):

new MapMaker().weakKeys().makeMap() 

This means once no one else is holding onto the Thread (implying it's finished) the key/value can be garbage collected, which is an improvement, but still doesn't address the thread contention issue, meaning so far our ThreadLocal isn't all that amazing of a class. Furthermore, if someone decided to hold onto Thread objects after they'd finished, they'd never be GC'ed, and therefore neither would our objects, even though they're technically unreachable now.

The Clever Implementation

We've been thinking about ThreadLocal as a mapping of threads to values, but maybe that's not actually the right way to think about it. Instead of thinking of it as a mapping from Threads to values in each ThreadLocal object, what if we thought about it as a mapping of ThreadLocal objects to values in each Thread? If each thread stores the mapping, and ThreadLocal merely provides a nice interface into that mapping, we can avoid all of the issues of the previous implementations.

An implementation would look something like this:

// called for each thread, and updated by the ThreadLocal instance new WeakHashMap<ThreadLocal,T>() 

There's no need to worry about concurrency here, because only one thread will ever be accessing this map.

The Java devs have a major advantage over us here - they can directly develop the Thread class and add fields and operations to it, and that's exactly what they've done.

In java.lang.Thread there's the following lines:

/* ThreadLocal values pertaining to this thread. This map is maintained  * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; 

Which as the comment suggests is indeed a package-private mapping of all values being tracked by ThreadLocal objects for this Thread. The implementation of ThreadLocalMap is not a WeakHashMap, but it follows the same basic contract, including holding its keys by weak reference.

ThreadLocal.get() is then implemented like so:

public T get() {     Thread t = Thread.currentThread();     ThreadLocalMap map = getMap(t);     if (map != null) {         ThreadLocalMap.Entry e = map.getEntry(this);         if (e != null) {             @SuppressWarnings("unchecked")             T result = (T)e.value;             return result;         }     }     return setInitialValue(); } 

And ThreadLocal.setInitialValue() like so:

private T setInitialValue() {     T value = initialValue();     Thread t = Thread.currentThread();     ThreadLocalMap map = getMap(t);     if (map != null)         map.set(this, value);     else         createMap(t, value);     return value; } 

Essentially, use a map in this Thread to hold all our ThreadLocal objects. This way, we never need to worry about the values in other Threads (ThreadLocal literally can only access the values in the current Thread) and therefore have no concurrency issues. Furthermore, once the Thread is done, its map will automatically be GC'ed and all the local objects will be cleaned up. Even if the Thread is held onto, the ThreadLocal objects are held by weak reference, and can be cleaned up as soon as the ThreadLocal object goes out of scope.


Needless to say, I was rather impressed by this implementation, it quite elegantly gets around a lot of concurrency issues (admittedly by taking advantage of being part of core Java, but that's forgivable them since it's such a clever class) and allows for fast and thread-safe access to objects that only need to be accessed by one thread at a time.

tl;dr ThreadLocal's implementation is pretty cool, and much faster/smarter than you might think at first glance.

If you liked this answer you might also appreciate my (less detailed) discussion of ThreadLocalRandom.

Thread/ThreadLocal code snippets taken from Oracle/OpenJDK's implementation of Java 8.

like image 193
dimo414 Avatar answered Oct 08 '22 06:10

dimo414