first, what is threadlocal
ThreadLocal, many places are called thread-local variables, and some places are called thread-local storage, in fact, the meaning is similar. Many bloggers say this: Threadlocal provides a new way to solve the concurrency problem of multi-thread programming, threadlocal is to solve the problem of sharing the multi-thread access resources. But that's not exactly the way to say it. Threadlocal is a replica created for a variable in each thread, which means that each thread can access its own internal replica variable by creating a new object of the same content, each thread creates one, rather than using the object's reference, through the new action in each thread. See how the source code in the JDK describes threadlocal:
This class provides thread-local variables. These variables differ fromtheir normal counterparts in so each thread that accesses one (via its{@code get} or {@code s ET} method) has its own, independently initializedcopy of the variable. {@code ThreadLocal} instances is typically privatestatic fields in classes the wish to associate state with a thread (E. G.,a User ID or Transaction ID).
This means that the Threadlocal class is used to provide local variables inside the thread. This variable is accessed in a multithreaded environment (accessed through a Get or set method) to ensure that the variables of each line thread are relatively independent of variables within other threads. Threadlocal instances are typically private static types that are used to correlate threads and thread contexts. That is, the role of threadlocal is to provide local variables within the thread, which function in the life cycle of the threads, reducing the complexity of the transfer of some common variables within the same thread, as opposed to the role of variable isolation. It's literally easy to understand, but it's not that easy to really understand. Let's look at an example first:
Class ConnectionManager { private static Connection connect = NULL; public static Connection OpenConnection () { if (connect = = NULL) { connect = drivermanager.getconnection (); } return connect; } public static void CloseConnection () { if (connect!=null) connect.close ();} }
Suppose there is a database link management class that has no problem using this code in a single thread, but what if it is used in multi-threading? Obviously, there is a thread-safety issue in using multiple threads:
First, none of the 2 methods are synchronized, and it is likely that connect will be created multiple times in the OpenConnection method;
Second, because connect is a shared variable, it is necessary to use synchronization to ensure thread safety where connect is called, because it is likely that one thread is using Connect for database operations, while another thread calls CloseConnection to close the link.
Therefore, for thread-safety reasons, the two methods of this code must be synchronized, and synchronous processing is required where connect is called.
This will greatly affect the efficiency of the execution of the program, because when a thread uses connect for database operations, the other threads only wait.
So let's take a closer look at this question, does this place need to be shared with the connect variable? In fact, it is not necessary. If there is a connect variable in each thread, access to the connect variable between the threads is actually not dependent, i.e. one thread does not need to be concerned about whether the other thread has modified the connect.
Here, a friend may think that since you do not need to share this variable between threads, you can do this directly, create a database link in each method that needs to use the database connection, and then release the connection after the method call is complete. such as the following:
class ConnectionManager {private Connection connect = null; Public Connection OpenConnection () {if (connect = = NULL) {connect = Drivermanager.getconnection (); } return connect; } public void CloseConnection () {if (connect!=null) connect.close (); }} class dao{public void Insert () {ConnectionManager ConnectionManager = new ConnectionManager (); Connection Connection = Connectionmanager.openconnection (); Use connection to operate connectionmanager.closeconnection (); }}
There is really no problem with this processing, because each time a connection is created inside a method, there is no thread-safety problem between threads. However, this can have a fatal impact: causing the server to be very stressful and severely impacting program execution performance. Because the database connection needs to be opened and closed frequently in the method, this can not seriously affect the efficiency of the program execution, but also may cause the server pressure is huge.
It would be fitting to use threadlocal in this case, because threadlocal creates a copy of the variable in each thread, that is, there is a variable inside each thread that can be used anywhere inside the thread, and the threads do not affect each other. As a result, there is no thread-safety issue, and there is no serious impact on program execution performance.
Note, however, that although threadlocal is able to solve the above problem, due to the creation of replicas in each thread, it is important to consider the consumption of resources, such as memory usage, which is larger than threadlocal.
second, how to realize threadlocal
If let everybody design threadlocal, how will everybody design? I believe most people will have this idea: each threadlocal class creates a map, and then uses the thread ID as the map key, the instance object as the value of the map, so that the value of each thread can be isolated. Yes, this is the simplest design, as the earliest threadlocal of the JDK was designed to do. JDK1.3 after the threadlocal design changed a way. Let's take a look at how threadlocal is currently designed:
Constructor: Threadlocal's constructor signature is this, and nothing inside is done:
/** * Creates a thread local variable. * @see #withInitial (java.util.function.Supplier) */public ThreadLocal () {}
the Threadlocal class provides several methods:
Public T get () {}public void set (T value) {}public void Remove () {}protected T InitialValue () {}
The Get () method is used to get a copy of the variable that threadlocal holds in the current thread, and set () sets a copy of the variable in the current thread to remove the copy of the variable in the current thread, InitialValue () is a protected method, which is typically used for rewriting when used, and it is a lazy loading method, which is described in detail below.
First, let's take a look at how the Threadlocal class creates a copy of a variable for each thread.
First look at the implementation of the Get method:
Public T get () { Thread 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; return result; } } return Setinitialvalue (); }
The first sentence is to obtain the current thread, and then get to a map,map type of Threadlocalmap by Getmap (t) method. Then get to the <key,value> key-value pair, and note that this is what gets the key-value pair, not the current thread T.
If successful, the value is returned.
If map is empty, call the Setinitialvalue method to return value.
each of our above sentences is carefully analyzed:
first look at what the Getmap method does:
Threadlocalmap Getmap (Thread t) { return t.threadlocals;}
What might not have occurred to you is that in Getmap, it is called the current thread T, which returns a member variable threadlocals in the present thread T.
so let's go ahead and take a look at the member variable threadlocals in the thread class:
Threadlocal.threadlocalmap threadlocals = null;
Is actually a threadlocalmap, this type is an inner class of the Threadlocal class, we continue to take a look at the implementation of Threadlocalmap, get () the specific implementation of the Setinitialvalue method:
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;}
It is easy to understand, that is, if the map is not empty, set the key value pairs, empty, and then create a map, look at the implementation of Createmap:
void Createmap (Thread T, T firstvalue) { t.threadlocals = new Threadlocalmap (this, firstvalue);}
at this point, most of your friends may have understood how threadlocal creates a copy of a variable for each thread:
First, Within each thread, there is a member variable threadlocals of type Threadlocal.threadlocalmap, which is used to store the actual copy of the variable, the key value is the current threadlocal variable, and value is a copy of the variable ( Variable of type T).
initially, in thread, Threadlocals is empty, and when the Get () method or set () method is called through the threadlocal variable, the threadlocals in the thread class is initialized. And with the current threadlocal variable as the key value, to threadlocal the copy variable to save as value, save to Threadlocals.
then in the current thread, if you want to use a copy variable, you can find it in the threadlocals by using the Get method.
Here's an example of how the threadlocal can achieve the effect of creating a copy of a variable in each thread:
public class Test {threadlocal<long> longlocal = new threadlocal<long> (); threadlocal<string> stringlocal = new threadlocal<string> (); public void Set () {Longlocal.set (Thread.CurrentThread (). GetId ()); Stringlocal.set (Thread.CurrentThread (). GetName ()); } public Long Getlong () {return longlocal.get (); } public String getString () {return stringlocal.get (); public static void Main (string[] args) throws interruptedexception {final Test test = new Test (); Test.set (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); Thread thread1 = new Thread () {public void run () {test.set (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); }; }; Thread1.start (); Thread1.join (); System. Out.println (Test.getlong ()); System.out.println (Test.getstring ()); }}
The output of this code is:
From the output of this code, it can be seen that in the main thread and the THREAD1 thread, the longlocal saved copy values and Stringlocal saved copy values are not the same. The last time the copy value is printed again on the main thread is to prove that the replica values in the main thread and the THREAD1 thread are indeed different.
To summarize:
1) The actual copy created through threadlocal is stored in each thread's own threadlocals;
2) Why the Threadlocals type Threadlocalmap's key value is Threadlocal object, because there can be more than one threadlocal variable in each thread, just like longlocal and stringlocal in the code above;
3) before the GET, must first set, otherwise it will report null pointer exception;
You must override the InitialValue () method if you want to have normal access without calling set before get.
Because in the above code analysis process, we found that if there is no first set, that is, the map can not find the corresponding storage, then by calling the Setinitialvalue method to return I, and in the Setinitialvalue method, there is a statement is t value = InitialValue (), and by default, the InitialValue method returns NULL.
Look at the following example:
public class Test {threadlocal<long> longlocal = new threadlocal<long> (); threadlocal<string> stringlocal = new threadlocal<string> (); public void Set () {Longlocal.set (Thread.CurrentThread (). GetId ()); Stringlocal.set (Thread.CurrentThread (). GetName ()); } public Long Getlong () {return longlocal.get (); } public String getString () {return stringlocal.get (); public static void Main (string[] args) throws interruptedexception {final Test test = new Test (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); Thread thread1 = new Thread () {public void run () {test.set (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); }; }; Thread1.start (); Thread1.join (); System.out.println (Test.getlong ()); System.ouT.println (Test.getstring ()); }}
In the main thread, without the first set, direct get, the runtime will report a null pointer exception (this is not the exception that the source code throws.) This exception is because the Getlong method returned by itself is a long type, because we have not overridden the InitialValue method, which is null by default, so the Get method is called to return a null pointer that is thrown when the null,null strongly turns long. If it is returned to a long object, it is OK, but it is still rewritten.
However, if you change to the following code, the InitialValue method is overridden:
public class Test {threadlocal<long> longlocal = new threadlocal<long> () {protected Long Initialval UE () {return Thread.CurrentThread (). GetId (); }; }; threadlocal<string> stringlocal = new threadlocal<string> () {; Protected String InitialValue () {return Thread.CurrentThread (). GetName (); }; }; public void Set () {Longlocal.set (Thread.CurrentThread (). GetId ()); Stringlocal.set (Thread.CurrentThread (). GetName ()); } public Long Getlong () {return longlocal.get (); } public String getString () {return stringlocal.get (); public static void Main (string[] args) throws interruptedexception {final Test test = new Test (); Test.set (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); Thread thread1 = new Thread () {public void run () {test.set (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); }; }; Thread1.start (); Thread1.join (); System.out.println (Test.getlong ()); System.out.println (Test.getstring ()); }}
You can call get directly without first set.
You can summarize the design ideas of threadlocal:
Each thread maintains a THREADLOCALMAP mapping table, the key of which is the threadlocal instance itself, and value is the object that really needs to be stored.
The solution is just the opposite of the simple design we started talking about.
Look up the information, so the main design has the following advantages:
1, this design after each map entry number is smaller: Before the number of thread, now is the number of threadlocal, can improve performance, it is said that the performance of the promotion is not a little two points (because the larger map is basically a chain list more, although time-consuming. )
2, when the thread destroyed after the corresponding Threadlocalmap also destroyed, can reduce the amount of memory usage.
three, the threadlocal application scene
The most common threadlocal usage scenarios are used to resolve database connections, session management, and so on.
Such as:
private static threadlocal<connection> connectionholder= new Threadlocal<connection> () {public Connection InitialValue () { return drivermanager.getconnection (Db_url);}; public static Connection getconnection () {return connectionholder.get ();}
such as:
private static final ThreadLocal threadsession = new ThreadLocal (); public static session getsession () throws Infrastructureexception { session S = (session) Threadsession.get (); try { if (s = = = null) { s = getsessionfactory (). Opensession (); Threadsession.set (s); } } catch (Hibernateexception ex) { throw new infrastructureexception (ex); } return s;}
Iv. Whether the threadlocal will cause a memory leak
Let's first tell the truth: Threadlocalmap is using the weak reference of threadlocal as key:
Static class Threadlocalmap { /** * The entries in this hash map extend WeakReference, using * Its main ref fi Eld as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get () * = = NULL) mean that the key is no longer referenced and so the * entry can Expunged from table. Such entries is referred to * as "stale entries" in the code that follows. */ Static class Entry extends Weakreference<threadlocal<?>> { /** the value associated with this Th Readlocal. */ Object value; Entry (threadlocal<?> K, Object v) { super (k); Value = V; } } ... ...}
is a reference graph between some of the objects that this article describes, solid lines represent strong references, and dashed lines represent weak references:
And then online rumors that Threadlocal would trigger a memory leak, for their reasons:
For example, Threadlocalmap uses the weak reference of threadlocal as key, if a threadlocal does not have an external strong reference to him, then the system GC, this threadlocal is bound to be recycled, in this way, Threadlocalmap will appear in the key null entry, there is no way to access these key null entry value, if the current thread no longer end, The value of these key null entry will always have a strong reference chain:
Thread Ref, Thread, Threalocalmap, Entry, value
Can never be recycled, causing memory leaks.
Let's see if this will happen.
In fact, in the JDK Threadlocalmap design has taken into account this situation, but also added some protection measures, the following is the source of Threadlocalmap Getentry method:
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);}
source code for the Getentryaftermiss function:
Private Entry Getentryaftermiss (threadlocal<?> key, int i, Entry e) { entry[] tab = table; int len = tab.length; while (E! = null) { threadlocal<?> k = E.get (); if (k = = key) return e; if (k = = null) expungestaleentry (i); else i = Nextindex (i, Len); e = Tab[i]; } return null; }
source code for the Expungestaleentry function:
private int expungestaleentry (int staleslot) {entry[] tab = table; int len = tab.length; Expunge entry at Staleslot tab[staleslot].value = null; Tab[staleslot] = null; size--; Rehash until we encounter null Entry e; int i; for (i = Nextindex (Staleslot, Len); (e = Tab[i])! = NULL; i = Nextindex (i, Len)) {threadlocal<?> k = E.get (); if (k = = null) {e.value = null; Tab[i] = null; size--; } else {int h = k.threadlocalhashcode & (len-1); if (h! = i) {tab[i] = null; Unlike Knuth 6.4 algorithm R, we must scan until//NULL because multiple entries could have been Stale. while (tab[h]! = null) H = nExtindex (H, Len); TAB[H] = e; }}} return i; }
Tidy up the flow of Threadlocalmap's Getentry function:
1, first from the direct index position of the threadlocal (through the Threadlocal.threadlocalhashcode & (len-1) operation obtained) entry E, if E is not null and key is the same return e;
2, if E is null or key inconsistency is queried to the next location, if the next key and the current need to query the key is equal, then the corresponding entry is returned, otherwise, if the key value is null, then the location of the entry is erased, otherwise continue to the next location query.
In this process encountered the key is NULL entry will be erased, then the value within the entry will not have a strong reference chain, will naturally be recycled. Careful study of the code can be found that the set operation has similar ideas, the key is NULL for these entry are removed, to prevent memory leaks.
But the light is not enough, the above design mentality relies on a precondition: to call Threadlocalmap's getentry function or set function. This is certainly not possible in any case, so in many cases it is necessary for the user to manually call Threadlocal's remove function and manually remove the threadlocal that are no longer needed to prevent a memory leak. Therefore, the JDK recommends that the threadlocal variable be defined as private static, so that the threadlocal life cycle is longer, because there is always a strong reference threadlocal, so threadlocal will not be recycled, You can also guarantee that the value of entry can be accessed at any time based on the weak reference of threadlocal, then remove it to prevent memory leaks.
Rookie's road--java concurrency of Threadlocal