We are all in the gutter, but some of us are looking at the stars
We are all in the gutter, but some of us are looking at the stars
Today we’re going to talk about ThreadLocal and how it works. I’m going to talk about ThreadLocal and how it works
ThreadLocal is an internal storage class that can store data in a specified thread. Once the data is stored, only the specified thread can get the stored data
In the following example, we start two threads, set the value of the local variable inside each thread, and then call print to print the value of the current local variable. Calling the remove method of local variables after printing removes variables from local memory, as shown below
public class ThreadLocalMain {
static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static void print(String str){
System.out.println(str + ":"+threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args){
Thread thread1 = new Thread(new Runnable() {
@Override
public void runThreadlocal.set () {// set threadlocal.set ();"thread1");
print("thread1");
System.out.println("after remove+"+threadLocal.get()); }}); Thread thread2 = new Thread(newRunnable() {
@Override
public void runThreadlocal.set () {// set threadlocal.set ();"thread2");
print("thread2");
System.out.println("after remove+"+threadLocal.get()); }}); thread1.start(); thread2.start(); }Copy the code
Thread1 :thread1 Thread2 :thread2 after remove+null After remove+nullCopy the code
ThreadLocal implementation principle:
Let’s start with a few threadLocal methods:
public void setT = thread.currentThread (); ThreadLocalMap map = getMap(t); // If there is a mapsetIf no, create a map andset
if(map ! = null) map.set(this, value);else
createMap(t, value);
}
public T getThread 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;
returnresult; }}return setInitialValue(); } //getMap getMap(Thread t) {// Get the Thread's own variable threadLocals and bind it to the member variable threadLocals of the current calling Threadreturnt.threadLocals; } //createMap void createMap(Thread t, t firstValue) { ThreadLocals. Htreadlocals = new ThreadLocalMap(this, firstValue); }Copy the code
Each thread holds a ThreadLocalMap object. Each new Thread instantiates a ThreadLocalMap and assigns it to the member variable threadLocals. If threadLocals already exists, the existing object will be used.
If the getMap method does not return null, the value is directly set to threadLocals (key is the current thread reference and value is a local variable). If the getMap method returns null, it is the first call to the set method (as mentioned earlier, the default value of threadLocals is null, and the map is created only when the set method is called). In this case, the createMap method is called to create threadLocals
CreateMap void createMap(Thread t, t firstValue) { ThreadLocals. Htreadlocals = new ThreadLocalMap(this, firstValue); }Copy the code
The createMap method not only creates threadLocals, but also adds local variable values to threadLocals
Get method
In the implementation of the get method, the current caller thread is first obtained. If threadLocals of the current thread is not null, the value of the local variable bound by the current thread is returned directly. Otherwise, the setInitialValue method is used to initialize the threadLocals variable. The setInitialValue method, similar to the set method, determines whether the threadLocals variable of the current thread is null. If it is, the local variable is added. Otherwise, the threadLocals variable is created. The same added value is null.
public T getThread t = thread.currentThread (); ThreadLocalMap map = getMap(t); // If threadlocals is not empty, the value of the local variable will be found in the mapif(map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this);if(e ! = null) { @SuppressWarnings("unchecked")
T result = (T)e.value;
returnresult; }} // At this point, threadLocals is null. Call the threadLocals variable that initializes the current threadreturn setInitialValue();
}Copy the code
The implementation of remove method
public void removeThreadLocals ThreadLocalMap m = getMap(thread.currentThread ()); // If map is not null, the local variable of the specified ThreadLocal instance in the current thread is removedif(m ! = null) m.remove(this); }Copy the code
Why remove after each use
If the current thread persists without calling Threadlocal’s remove method, and there are references to Threadlocal elsewhere, The ThreadLocalMap variable of the current thread contains references to ThreadLocal variables and references to value objects that will not be released, causing a memory leak. But consider that if the ThreadLocal variable has no other strong dependencies and the current thread still exists, since the key in the thread’s ThreadLocalMap is weak reference,
Weak references to ThreadLocal variables in the current thread’s ThreadLocalMap will be reclaimed during GC, but the corresponding values will still leak memoryIn ThreadLocalMap, there will be an entry whose key is null but value is not null. So, remember remove to avoid memory leakage
Why weak references
- Key uses strong references: The object referenced by ThreadLocal is reclaimed, but ThreadLocalMap also holds a strong reference to ThreadLocal. Without manual deletion, ThreadLocal will not be reclaimed, resulting in Entry memory leaks.
- Key uses weak references: The object referenced by ThreadLocal is reclaimed, and since ThreadLocalMap holds a weak reference to ThreadLocal, ThreadLocal is reclaimed even without manual deletion. The value is cleared the next time ThreadLocalMap calls set,get, and remove.