How is Java ThreadLocal implemented under the hood?

How is ThreadLocal implemented? Is it implemented in Java (using some parallel card from ThreadID to object) or uses some kind of JVM hook for more efficient use?

+61
java multithreading thread-local thread-static
Jul 29 '09 at 19:16
source share
5 answers

All the answers here are correct, but a little disappointing, as they are somewhat silent as a smart implementation of ThreadLocal . I just watched the source code for ThreadLocal and was pleasantly impressed with how it was implemented.

Naive implementation

If I asked you to implement the ThreadLocal<T> class taking into account the API described in javadoc, what would you do? The initial implementation will most likely be ConcurrentHashMap<Thread,T> using Thread.currentThread() as its key. This will work quite well, but has some disadvantages.

  • Competition on the subject - ConcurrentHashMap - is a pretty smart class, but in the end it should still deal with preventing the rotation of threads from it in any way, and if different threads hit it regularly, it will slow down.
  • Constantly saves a pointer to both the Thread and the object, even after the completion of Thread and can be GC'ed.

GC compliant implementation

Try again, let's look at the problem of garbage collection using weak links . Working with WeakReferences can be confusing, but it should be enough to use a map constructed in this way:

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

Or, if we use Guava (and we should be!):

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

This means that after no one holds the Thread (assuming it is finished), the key / value can be garbage collected, which is an improvement, but still does not address the thread conflict problem, which means that our ThreadLocal not everything what an amazing class. In addition, if someone decided to hold Thread objects after they are finished, they will never be GC'ed, and therefore our objects, even if they are not technically available, will no longer be.

Smart implementation

We thought of ThreadLocal as mapping threads to values, but perhaps this is not the right way to think about it. Instead of thinking of it as matching threads with values ​​in each ThreadLocal object, what if we thought of it as matching ThreadLocal objects with values ​​in each thread? If each thread stores a mapping, and ThreadLocal just provides a good interface in this mapping, we can avoid all the problems of previous implementations.

The implementation will look something like this:

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

There is no need to worry about concurrency, because only one thread will ever access this card.

Java developers have a major advantage over us: they can directly develop the Thread class and add fields and operations to it, and exactly what they did.

There are the following lines in java.lang.Thread :

 /* 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 batch-private display of all the values ​​tracked by ThreadLocal objects for this Thread . The ThreadLocalMap implementation is not WeakHashMap , but it corresponds to the same main contract, including holding its keys by a weak link.

ThreadLocal.get() then executed as follows:

 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 this:

 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 of our ThreadLocal objects. Thus, we do not need to worry about the values ​​in other Threads ( ThreadLocal can literally only access values ​​in the current thread) and therefore has no concurrency problems. In addition, as soon as Thread is completed, its map will automatically be GC'ed, and all local objects will be cleared. Even if a Thread held, ThreadLocal objects are retained by a weak standard and can be cleared as soon as the ThreadLocal object goes out of scope.




Needless to say, I was impressed with this implementation, it pretty elegantly circumvents a lot of concurrency issues (admittedly, taking advantage of being part of the main Java, but it forgives them, since it is such a smart class) and provides fast and thread-safe access to objects that need to be accessed only once by a single thread.

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

If you liked this answer, you can also rate my (less detailed) discussion of ThreadLocalRandom .

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

+84
Mar 27 '13 at 6:25
source share

You mean java.lang.ThreadLocal . It's pretty simple, really, it's just a map of name-value pairs stored inside each Thread object (see Thread.threadLocals field). The API hides this implementation detail, but that's more or less all there is.

+29
Jul 29 '09 at 19:20
source share

ThreadLocal variables in Java work by accessing the HashMap stored in the Thread.currentThread () instance.

+7
Jul 29 '09 at 19:20
source share

Suppose you are going to implement ThreadLocal , how do you make it a specific thread? Of course, the easiest way is to create a non-static field in the Thread class, let's call it threadLocals . Since each thread is represented by a thread instance, therefore threadLocals in each thread will also be different. And this is also what Java does:

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

What is ThreadLocal.ThreadLocalMap here? Because you only have threadLocals for the thread, so if you just take threadLocals as your ThreadLocal (say, define threadLocals as Integer ), you will only have one ThreadLocal for a specific thread. What if you need several ThreadLocal variables for a thread? The easiest way is to make threadLocals a HashMap , the key each record is the name of the ThreadLocal variable, and the value each record is the value of the ThreadLocal variable. A little confused? Let them say that we have two streams, t1 and t2 . they take the same Runnable instance as a parameter to the Thread constructor, and both of them have two ThreadLocal variables named tlA and tlb . This is what you like.

t1.tlA

 +-----+-------+ | Key | Value | +-----+-------+ | tlA | 0 | | tlB | 1 | +-----+-------+ 

t2.tlB

 +-----+-------+ | Key | Value | +-----+-------+ | tlA | 2 | | tlB | 3 | +-----+-------+ 

Please note that the values ​​are compiled by me.

Now that seems perfect. But what is ThreadLocal.ThreadLocalMap ? Why didn't he just use a HashMap ? To solve the problem, let's see what happens when we set the value through the set(T value) method of the ThreadLocal class:

 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } 

getMap(t) just returns t.threadLocals . Since t.threadLocals was initialized to null , so first enter createMap(t, value) :

 void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } 

Creates a new instance of ThreadLocalMap using the current instance of ThreadLocal and the value to be set. Let's see what ThreadLocalMap , it's actually part of the ThreadLocal class

 static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (ie entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } ... /** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } ... } 

The main part of the ThreadLocalMap class is the Entry class , which extends WeakReference . This ensures that if the current thread comes out, it will be automatically garbage collected. That is why it uses ThreadLocalMap instead of a simple HashMap . It passes the current ThreadLocal and its value as an parameter of the Entry class, so when we want to get this value, we can get it from table , which is an instance of the Entry class:

 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(); } 

This is what looks like the whole picture:

Whole image

+1
Nov 12 '15 at 16:23
source share

Conceptually, you can think of ThreadLocal<T> as a Map<Thread,T> that stores thread-specific values, although this is not the way it is actually implemented.

Thread-specific values ​​are stored in the Thread object itself; when the thread completes, the values ​​of the thread may be garbage collected.

Link: JCIP

0
Nov 29 '17 at 21:41
source share



All Articles