Interpretation of ThreadLocal
Basic test usage code
public class ThreadLoaclDemo { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); static String test = "hello"; public static void main(String[] args) { new Thread(() ->{ threadLocal.set("hello threadLocal1"); test += "1"; System.out.println(threadLocal.get()); System.out.println(test); }).start(); new Thread(() ->{ threadLocal.set("hello threadLocal2"); test += "2"; //threadLocal.remove(); Clears the localVariable in the local memory of the current thread System.out.println(threadLocal.get()); System.out.println(test); }).start(); } }
We usually introduce ThradLocal to consider such scenarios
When multithreading processes shared variables, there will be security problems. In order to ensure security, appropriate synchronization operations are usually carried out
The general measure of synchronization is locking
While locking ensures security, it also has related efficiency and concurrency problems
Is there such a variable that the thread uses its own when it gets it?
Following this idea, it is easy to feel that if we use ThreadLocal, the local variables of the thread are stored in the ThreadLocal instance. Each thread accesses its own variables by going to ThreadLocal to get data through the current thread instance.
Not really
The local variable of the thread is stored in the threadlocals of the current thread (calling thread)
Through the source code, we find that there are two variables in the Thread class
ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
It is null by default, but will not be created until the thread first calls the set or get method of ThradLocal class
First analyze the set, get and remove methods
public T get() { // Get current thread Thread t = Thread.currentThread(); // Get the threadLocalMap threadLocals of the current user thread ThreadLocalMap map = getMap(t); // If threadLocals is not empty if (map != null) { // Get the value in the local variable of the current thread from threadLocals // key is the current ThreadLocal instance ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // If threadLocals is null, the threadLocals member variable of the current thread is initialized return setInitialValue(); }
–
private T setInitialValue() { // Initialize to null T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } return value; }
Through the source code discovery, the first step is to obtain the current thread. The current thread is used to call the getMap method to obtain the current thread's own variable (threadlocal), which is bound to the thread's member variable
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
Look at set, the source code is similar
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { // Line 424 // Store values in threadlocals map.set(this, value); } else { createMap(t, value); } }
The remove method removes the ThreadLocal instance in the threadlocals of the current thread
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
Through these three methods, we can know that ThreadLocal actually acts as a tool class
It has not manipulated the elements in this class too much
Instead, use the threadlocals in the current thread in a disguised way
He calls the set method, stores value in the threadlocals of the calling thread, and calls the get method to extract it.
If the calling thread does not terminate all the time, the variables in the existing threadlocks will not be removed, which may lead to memory overflow. Therefore, after application, you need to call the remove method in ThreadLocal to delete the local variables in the threadlocks of the corresponding thread
Why map structure?
Because there is more than one ThreadLocal variable per thread
ThreadLocal also has a short board, that is, ThreadLocal sharing cannot be used in parent-child threads
I think this is not a short board of ThreadLocal, because ThreadLocal operations are based on the current thread
Then InheritableThreadLocal is born by luck
There are only three methods in the source code, that is, the three methods of the rewritten ThreadLocal
public class InheritableThreadLocal<T> extends ThreadLocal<T> { protected T childValue(T parentValue) { return parentValue; } /** * Get inheritableThreadLocals */ ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } /** * Override the createMap method to create inheritablethreadlocales instead of threadlocales */ void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
To understand how InheritableThreadLocal allows child threads to access parent Thread variables, you need to start with the Thread constructor
public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
track
/** Initializes the thread with the current AccessControlContext.*/ private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); }
track
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; // Get current thread Thread parent = currentThread(); /* Partial code omission */ /*Start entering the parent-child thread section*/ if (inheritThreadLocals && parent.inheritableThreadLocals != null) { // If the inheritableThreadLocals of the parent thread is not empty // Then set the inheritableThreadLocals variable in the child thread // Using the inheritableThreadLocals of the parent thread as the constructor, create a new ThreadLocalMap variable and assign it to the inheritableThreadLocals of the child thread this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); } this.stackSize = stackSize; tid = nextThreadID(); }
Trace createInheritedMap
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
Then go to ThreadLocalMap to see how it is constructed through the ThreadLocalMap of the parent thread
/** * Construct a new map containing all inheritable threadlocales from the given parent map * Called only by createInheritedMap */ private ThreadLocalMap(ThreadLocalMap parentMap) { // Get entity Entry[] parentTable = parentMap.table; // Get quantity int len = parentTable.length; setThreshold(len); table = new Entry[len]; // Copy, repackage for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { // Here is the childValue method overridden by calling InheritableThreadLocal Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) { h = nextIndex(h, len); } table[h] = c; size++; } } } }
That's it
When the parent thread creates a child thread, the constructor will copy the local variable of inheritableThreadLocals in the parent thread and save it in inheritableThreadLocals in the child thread
Usage scenario of inheritableThreadLocals
The child thread needs to use the user login information stored in the parent thread
The middleware needs to record the whole call link of the unified id trace
The beauty of java Concurrent Programming