What is ThreadLocal
ThreadLocal, the thread local variable. If you create a ThreadLocal variable, each thread accessing this variable will have a local copy of this variable. When multiple threads operate this variable, they actually operate the variables in their own local memory, so as to play the role of thread isolation and avoid thread safety problems.
ThreadLocal memory structure diagram
As can be seen from the memory structure diagram:
- Each Thread object holds a member variable of ThreadLocalMap.
- ThreadLocalMap internally maintains an Entry array. Each Entry represents a complete object. key is ThreadLocal itself and value is the generic value of ThreadLocal.
The above two points can be seen more clearly in the source code.
Important properties of ThreadLocal
In the source code, ThreadLocal has the following important attributes:
//The HashCode of the current ThreadLocal, calculated by nextHashCode() below, is used to calculate the index position of the current ThreadLocal in ThreadLocalMap. private final int threadLocalHashCode = nextHashCode(); //Ensure that the ThreadLocalHashCode of each ThreadLocal is unique private static AtomicInteger nextHashCode = new AtomicInteger(); //Hash magic number private static final int HASH_INCREMENT = 0x61c88647; //Used to return the calculated next hash value private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
Hash magic number hash_ The conversion of increment to decimal is 16405315272654435769, and the conversion to int type is - 16405315272654435769 is equal to (√ 5-1) / 2 multiplied by 2 to the 32nd power. (√ 5-1) / 2 is the golden section number, which is approximately 0.618.
In other words, 0x61c88647 can be understood as a golden section number multiplied by the 32nd power of 2, which can ensure that the hash value generated by nextHashCode is evenly distributed to the power of 2 and less than the 32nd power of 2.
Another important property of ThreadLocal is ThreadLocalMap.
ThreadLocalMap
ThreadLocalMap is a static internal class in ThreadLocal. Some source codes are as follows:
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { //value associated with the current thread Object value; //Create a key value pair with the key ThreadLocal Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //The initial capacity is 16 and must be a power of 2 private static final int INITIAL_CAPACITY = 16; //The length of the entity array of key value pairs must be a power of 2 private Entry[] table; //Length of ThreadLocalMap (number of elements) private int size = 0; //The threshold for capacity expansion is two-thirds of the array size by default private int threshold;
There is a static internal class entry in the source code. Entry inherits from WeakReference, that is, it is set as a weak reference. If it is set as a strong reference, even if ThreadLocal is set to null, GC will not recycle it.
From the source code, we can see that ThreadLocalMap is actually a simple Map structure with an array at the bottom, and each data is saved with an Entry. The key of Entry is the reference of ThreadLocal, and the value is the value of ThreadLocal.
set() method of ThreadLocal
The source code is as follows:
public void set(T value) { //Get current thread Thread t = Thread.currentThread(); //The current thread is Key. Find the corresponding ThreadLocalMap ThreadLocalMap map = getMap(t); //If the map is not null, it is directly added to the local variable. The Key is the current ThreadLocal and the value is the added value //If the map is null, create the corresponding map first if (map != null) { map.set(this, value); } else { createMap(t, value); } }
Get the current thread t first, and use the getMap() method to get the map corresponding to the current thread t. The source code of getMap() method is as follows:
ThreadLocalMap getMap(Thread t) { //Returns the current threadlocales return t.threadLocals; }
Then judge that the map called by getMap() method is not null, and directly set the value into the map; If it is null, the createMap() method is called to create a new map. The source code of the createMap() method is as follows:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
get() method of ThreadLocal
The source code is as follows:
public T get() { //Get current thread Thread t = Thread.currentThread(); //The current thread is Key. Find the corresponding ThreadLocalMap ThreadLocalMap map = getMap(t); //If the map is not empty, find the corresponding Entry through the map, and then find the value through the Key of the Entry and return if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //If the map is empty, it is initialized return setInitialValue(); }
Get the current thread t first, and use the getMap() method to get the map corresponding to the current thread t. If the map exists, obtain the value value corresponding to the current ThreadLocal; If the map does not exist, the setinitialvalue () method is called for initialization. The source code of setInitialValue() is as follows:
private T setInitialValue() { //initialValue() returns null, that is, set value to null T value = initialValue(); //Get current thread Thread t = Thread.currentThread(); //The current thread is Key. Find the corresponding ThreadLocalMap ThreadLocalMap map = getMap(t); //If the map is not null, it is directly added to the local variable. The Key is the current ThreadLocal and the value is the added value //If the map is null, create the corresponding map first if (map != null) { map.set(this, value); } else { createMap(t, value); } return value; }
remove() method of ThreadLocal
The source code is as follows:
public void remove() { //Gets the ThreadLocalMap of the current thread ThreadLocalMap m = getMap(Thread.currentThread()); //If the map exists, the remove() method of ThreadLocalMap is called if (m != null) { m.remove(this); } }
First extract the corresponding map from the current thread, then call the remove() method of ThreadLocalMap to delete the elements in map.
ThreadLocal memory leak
When ThreadLocal has no external strong reference, it will be recycled during GC, so the key value saved in ThreadLocalMap becomes null, and the Entry is referenced by ThreadLocalMap object, and ThreadLocalMap object is referenced by Thread object. Then, if the Thread does not terminate, the value object will always exist in memory, which will lead to memory leakage, The Thread will not be recycled until it is destroyed.
After using the ThreadLocal variable, we need to manually remove it to prevent the Entry in ThreadLocalMap from maintaining a strong reference to value, so that value can not be recycled, so as to avoid memory leakage.
summary
- Each thread has its own ThreadLocalMap.
- ThreadLocalMlap internally maintains an Entry array. Each Entry represents a complete object. key is ThreadLocal itself and value is the generic value of ThreadLocal.
- When setting a value in ThreadLocal, each thread stores it in its own ThreadLocalMap. Reading also takes a ThreadLocal as a reference and finds the corresponding key in its own map, so as to realize thread isolation.
reference
https://baijiahao.baidu.com/s?id=1663127810801876375&wfr=spider&for=pc
https://www.cnblogs.com/fsmly/p/11020641.html