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 .
4.map 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
2.map Non empty , Then, the ThreadLocal And the new value Copy into map in .
3.map 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

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 .

summary

* 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 :https://www.jianshu.com/p/98b68c97df9b
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 .