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?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
ThreadLocal variables in Java works by accessing a HashMap held by the Thread.currentThread() instance.
You mean
java.lang.ThreadLocal
. It's quite simple, really, it's just a Map of name-value pairs stored inside eachThread
object (see theThread.threadLocals
field). The API hides that implementation detail, but that's more or less all there is to it.Conceptually, you can think of a
ThreadLocal<T>
as holding aMap<Thread,T>
that stores the thread-specific values, though this is not how it is actually implemented.The thread-specific values are stored in the Thread object itself; when the thread terminates, the thread-specific values can be garbage collected.
Reference : JCIP
Suppose you're going to implement
ThreadLocal
, how do you make it thread-specific? Of course the simplest method is to create a non-static field in the Thread class, let's call itthreadLocals
. Because each thread is represented by a thread instance, sothreadLocals
in every thread would be different, too. And this is also what Java does:What is
ThreadLocal.ThreadLocalMap
here? Because you only have athreadLocals
for a thread, so if you simply takethreadLocals
as yourThreadLocal
(say, define threadLocals asInteger
), you will only have oneThreadLocal
for a specific thread. What if you want multipleThreadLocal
variables for a thread? The simplest way is to makethreadLocals
aHashMap
, thekey
of each entry is the name of theThreadLocal
variable, and thevalue
of each entry is the value of theThreadLocal
variable. A little confusing? Let's say we have two threads,t1
andt2
. they take the sameRunnable
instance as the parameter ofThread
constructor, and they both have twoThreadLocal
variables namedtlA
andtlb
. This is what it's like.t1.tlA
t2.tlB
Notice that the values are made up by me.
Now it seems perfect. But what is
ThreadLocal.ThreadLocalMap
? Why didn't it just useHashMap
? To solve the problem, let's see what happens when we set a value through theset(T value)
method of theThreadLocal
class:getMap(t)
simply returnst.threadLocals
. Becauset.threadLocals
was initilized tonull
, so we entercreateMap(t, value)
first:It creates a new
ThreadLocalMap
instance using the currentThreadLocal
instance and the value to be set. Let's see whatThreadLocalMap
is like, it's in fact part of theThreadLocal
classThe core part of the
ThreadLocalMap
class is theEntry class
, which extendsWeakReference
. It ensures that if the current thread exits, it will be garbage collected automatically. This is why it usesThreadLocalMap
instead of a simpleHashMap
. It passes the currentThreadLocal
and its value as the parameter of theEntry
class, so when we want to get the value, we could get it fromtable
, which is an instance of theEntry
class:This is what is like in the whole picture:
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 forThreadLocal
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 aConcurrentHashMap<Thread,T>
usingThread.currentThread()
as its key. This will would work reasonably well but does have some disadvantages.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.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:
Or if we're using Guava (and we should be!):
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 ontoThread
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:
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:Which as the comment suggests is indeed a package-private mapping of all values being tracked by
ThreadLocal
objects for thisThread
. The implementation ofThreadLocalMap
is not aWeakHashMap
, but it follows the same basic contract, including holding its keys by weak reference.ThreadLocal.get()
is then implemented like so:And
ThreadLocal.setInitialValue()
like so: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 theThread
is done, its map will automatically be GC'ed and all the local objects will be cleaned up. Even if theThread
is held onto, theThreadLocal
objects are held by weak reference, and can be cleaned up as soon as theThreadLocal
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.