ThreadLocal What is it?

ThreadLocal Is a local thread copy variable utility class . It is mainly used to map the private thread and the replica object stored by the thread , Variables between threads do not interfere with each other , In high concurrency scenarios , Stateless calls can be implemented , It is especially suitable for the scenario that each thread can not rely on variable values to complete operations .

Starting with data structure

The figure below shows ThreadLocal Internal structure of

ThreadLocal Inside the structure

From the structure diagram above , We've seen it ThreadLocal The core mechanism of :

* each Thread Each thread has one Map.
* Map It stores thread local objects (key) And the variable copy of the thread (value)
* however ,Thread Internal Map By ThreadLocal Maintained , from ThreadLocal Responsible to map Gets and sets the variable value of the thread .
So for different threads , Every time the replica value is obtained , Other threads cannot get the copy value of the current thread , The isolation of replicas is formed , No interference .

Thread Thread internal Map It is described in the class as follows :
public class Thread implements Runnable { /* ThreadLocal values pertaining to
this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null; }
In depth analysis ThreadLocal

ThreadLocal Class provides the following core methods :
public T get() public void set(T value) public void remove()
* get() Method is used to get the copy variable value of the current thread .
* set() Method is used to save the copy variable value of the current thread .
* initialValue() Initial replica variable value for the current thread .
* remove() Method to remove the copy variable value of the current predecessor .
get() method
/** * Returns the value in the current thread's copy of this * thread-local
variable. If the variable has no value for the * current thread, it is first
initialized to the value returned * by an invocation of the {@link
#initialValue} method. * * @return the current thread's value of this
thread-local */ public T get() { Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e =
map.getEntry(this); if (e != null) return (T)e.value; } return
setInitialValue(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
private T setInitialValue() { T value = initialValue(); Thread t =
Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)
map.set(this, value); else createMap(t, value); return value; } protected T
initialValue() { return null; }
step :
1. Gets the current thread's ThreadLocalMap object threadLocals
2. from map Gets the thread stored K-V Entry node .
3. from Entry Node gets stored Value Copy value return . If it is empty, the initial value is returned null, That is, the copy of the thread variable is null, In the use of the need to pay attention to judgment NullPointerException.

set() method
/** * Sets the current thread's copy of this thread-local variable * to the
specified value. Most subclasses will have no need to * override this method,
relying solely on the {@link #initialValue} * method to set the values of
thread-locals. * * @param value the value to be stored in the current thread's
copy of * this thread-local. */ public void set(T value) { Thread t =
Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)
map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread
t) { return t.threadLocals; } void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); }
step :
1. Gets the member variable of the current thread map Non empty , Then, the ThreadLocal And the new value Copy into map in . empty , To the member variable of the thread ThreadLocalMap Create initialization , And will ThreadLocal and value Copy in map in .

remove() method
/** * Removes the current thread's value for this thread-local * variable. If
this thread-local variable is subsequently * {@linkplain #get read} by the
current thread, its value will be * reinitialized by invoking its {@link
#initialValue} method, * unless its value is {@linkplain #set set} by the
current thread * in the interim. This may result in multiple invocations of the
* <tt>initialValue</tt> method in the current thread. * * @since 1.5 */ public
void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m !=
null) m.remove(this); } ThreadLocalMap getMap(Thread t) { return
t.threadLocals; }
remove The method is simple , Do not repeat .


ThreadLocalMap yes ThreadLocal Inner class of , It didn't come true Map Interface , In an independent way Map Function of , Its internal Entry It is also independent .

ThreadLocalMap Class diagram


stay ThreadLocalMap in , It's also useful Entry To save K-V Structural data . however Entry in key It can only be ThreadLocal object , This is Entry The construction method of is restricted .
static class Entry extends WeakReference<ThreadLocal> { /** The value
associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object
v) { super(k); value = v; } }
Entry Inherited from WeakReference( Weak reference , The life cycle can only survive until the next time GC Front ), But only Key Is of weak reference type ,Value Not a weak reference .

ThreadLocalMap Member variables of :
static class ThreadLocalMap { /** * The initial capacity -- MUST be a power of
two. */ private static final int INITIAL_CAPACITY = 16; /** * The table,
resized as necessary. * table.length MUST always be a power of two. */ private
Entry[] table; /** * The number of entries in the table. */ private int size =
0; /** * The next size value at which to resize. */ private int threshold; //
Default to 0 }
Hash How to solve the conflict

and HashMap The biggest difference is that ,ThreadLocalMap The structure is very simple , No, next quote , in other words ThreadLocalMap To solve the problem Hash The way of conflict is not the way of linked list , It's linear detection , So called linear detection , It's based on the beginning key Of hashcode Value determines that the element is in the table Position in array , If you find that there are others in this position key The element of value is occupied , The fixed algorithm is used to find the next position of a certain step size , Judge in turn , Until you find a place to store it .

ThreadLocalMap solve Hash The way of conflict is a simple step plus 1 Or minus 1, Find the next adjacent location .
/** * Increment i modulo len. */ private static int nextIndex(int i, int len)
{ return ((i + 1 < len) ? i + 1 : 0); } /** * Decrement i modulo len. */
private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 :
len - 1); }

obviously ThreadLocalMap Linear detection is used to solve the problem Hash Conflict is inefficient , If there are a lot of different ones ThreadLocal Object into map Medium time send conflict , Or a second conflict , The efficiency is very low .

So here's a good suggestion : Each thread stores only one variable , In this way, all threads are stored in the map Medium Key It's all the same ThreadLocal, If a thread wants to save multiple variables , You need to create multiple ThreadLocal, Multiple ThreadLocal Put in Map It will increase greatly in the middle of the year Hash The possibility of conflict .

ThreadLocalMap The problem of

because ThreadLocalMap Of key Is a weak reference , and Value Is a strong reference . This leads to a problem ,ThreadLocal When there is no strong reference to an external object , happen GC Time weak reference Key It's going to be recycled , and Value It won't be recycled , If you create ThreadLocal Is running continuously , So this one Entry Object value It may not be recycled all the time , A memory leak occurred .

How to avoid leakage

since Key Is a weak reference , So what we need to do , Is calling ThreadLocal Of get(),set() Method, and then call the remove method , take Entry Node and Map Remove reference relation of , So the whole thing Entry Object in GC
Roots After analysis, it becomes inaccessible , next time GC They can be recycled .

If used ThreadLocal Of set After method , Calls not shown remove method , Memory leaks can occur , So it is very important to develop good programming habits , End of use ThreadLocal after , Remember to call remove method .
ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); try {
threadLocal.set(new Session(1, "Misout Blog of ")); // Other business logic } finally {
threadLocal.remove(); }
Application scenarios

Remember Hibernate Of session Get the scene ?
private static final ThreadLocal<Session> threadLocal = new
ThreadLocal<Session>(); // obtain Session public static Session getCurrentSession(){
Session session = threadLocal.get();
// judge Session Is it empty , If empty , Will create a session, And set it to the local thread variable try { if(session
==null&&!session.isOpen()){ if(sessionFactory==null){ rbuildSessionFactory();//
establish Hibernate Of SessionFactory }else{ session = sessionFactory.openSession(); } }
threadLocal.set(session); } catch (Exception e) { // TODO: handle exception }
return session; }

Why? ? Each thread accessing the database should be independent Session conversation , If multiple threads share the same Session conversation , It is possible that another thread has closed the connection , The session is closed when the current thread executes the commit again , Cause system abnormality . This method can avoid thread contention Session, Improve the security of concurrency .

use ThreadLocal The typical scenario of database connection management is shown above , Thread session management and other scenarios , Only for independent variable copies , If the variable is globally shared , It is not suitable for use under the high and low level .


* each ThreadLocal Only one copy of the variable can be saved , If you want to go online, a thread can save more than one copy , You need to create multiple ThreadLocal.
* ThreadLocal Internal ThreadLocalMap The key is a weak reference , There is a risk of memory leaks .
* For stateless applications , High concurrency scenarios with independent replica variables without affecting business logic . If the business logic is strongly dependent on replica variables , It is not suitable for use ThreadLocal solve , Need to find another solution .

author :Misout
link :
Source : Bamboo slips
The copyright of the simple book belongs to the author , Any form of reprint should contact the author to obtain authorization and indicate the source .