Introduction
No matter the actual project or the interview, ThreadLocal is an inextricable topic. This paper mainly discusses the mystery of ThreadLocal from the source point of view.
- What is ThreadLocal? What does it do?
- ThreadLocal source code analysis
- summary
1, What is ThreadLocal? What does it do?
ThreadLocal is a local variable of a thread, which means that this variable is unique to the thread and cannot be shared with other threads. It does not solve the problem of multi-threaded variable sharing.
Therefore, ThreadLocal is different from thread synchronization mechanism. Thread synchronization mechanism is that multiple threads share the same variable, and ThreadLocal is to create a separate variable copy for each thread. Therefore, each thread can independently change its own variable copy without affecting the copies corresponding to other threads. It can be said that ThreadLocal provides another way to solve the variable problem in multithreaded environment.
The idea of ThreadLocal is to trade space for time, so that each thread can access its own copy of variables. The values of variables do not interfere with each other, reducing the complexity of the transfer of some common variables between multiple functions or components in the same thread.
2, ThreadLocal source code analysis
1. ThreadLocalMap parsing
ThreadLocal defines an internal class of ThreadLocalMap. ThreadLocalMap actually uses Entry to store key value, as shown below:
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } ... }
ThreadLocalMap is the key to realize thread isolation mechanism. From the above code, we can see that the key of Entry is ThreadLocal, and the value is the value. At the same time, the Entry also inherits the WeakReference, so the reference of the key (ThreadLocal instance) corresponding to the Entry is a weak reference.
Let's look at the core getEntry(), set (ThreadLocal > key, object value) methods
private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
private void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; // Find the position of the corresponding element in the array according to the hash value of ThreadLocal int i = key.threadLocalHashCode & (len-1); // Use "linear detection method" to find the right position for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); // If key exists, directly overwrite if (k == key) { e.value = value; return; } // key == null, but there is a value (because here e! = null), indicating that the previous ThreadLocal object has been recycled if (k == null) { // Replace old elements with new ones replaceStaleEntry(key, value, i); return; } } //Create a new element tab[i] = new Entry(key, value); int sz = ++size; // rehash if stale entries are not cleaned up and the elements in the array are greater than the threshold if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
2. Core method analysis
(1) get()
Returns the value in the current thread copy of this thread local variable
public T get() { //Get current thread Thread t = Thread.currentThread(); //Get the member variable threadLocalMap of the current thread ThreadLocalMap map = getMap(t); if (map != null) { // Get the corresponding Entry from the ThreadLocalMap of the current thread ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") // Get target value T result = (T)e.value; return result; } } return setInitialValue(); }
First, get the corresponding member variable ThreadLocalMap through the current thread, then get the Entry of the current ThreadLocal through the ThreadLocalMap, and finally get the target value result through the obtained Entry.
(2) set(T value)
Sets the value in the current thread copy of this thread local variable to the specified value.
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
Get the ThreadLocalMap corresponding to the current thread. If it is not empty, call the set() method of ThreadLocalMap. The key is the current ThreadLocal. If it does not exist, call the createMap() method to create a new one.
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
(3) initialValue()
Returns the initial value of the current thread for this thread's local variable.
protected T initialValue() { return null; }
This method is defined as the protected level and returns null. Its subclass is required to implement its functions, so we should generally override this method when using ThreadLocal. This method can't display the call, it will be executed only when the get() or set() method is called for the first time, and only once.
(4) remove()
Remove the value of the current thread for this thread local variable.
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
Three, summary
1. When multiple threads get a shared variable, they need to get a copy of the initial value of the variable. Each thread stores a copy of this variable. Changes to the copy of this variable will not affect the variable itself. It is applicable to scenarios where multiple threads rely on different variable values to complete operations. For example:
- Switching of multiple data sources
- spring declarative transaction
2. Set ThreadLocal to private static, so that ThreadLocal will be recycled with the thread itself as much as possible.