Java concurrency programming: deep anatomy threadlocal
Presumably a lot of friends are not unfamiliar with threadlocal, today we come together to explore the use of threadlocal and the principle of implementation. First of all, this paper first discusses the understanding of threadlocal, and then according to the source code of the Threadlocal class analysis of its implementation principle and the use of the need to pay attention to the place, and finally gave two application scenarios.
The following is an outline of this article directory:
I. Understanding of the Threadlocal
Two. Deep analysis of the Threadlocal class
Three. Threadlocal's application Scenario
If there are any shortcomings please understand, and welcome criticism.
Please respect the author's labor results, reproduced please indicate the original link:
Http://www.cnblogs.com/dolphin0520/p/3920407.html
I. Understanding of the Threadlocal
ThreadLocal, many places are called thread-local variables, and some places are called thread-local storage, in fact, the meaning is similar. Maybe a lot of friends know that threadlocal a replica is created for a variable in each thread, each thread can access its own internal copy variable.
This sentence literally seems easy to understand, but the real understanding is not so easy.
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 problem with Multithreading: First, there are 2 methods that are not synchronized, and it is likely that connect is created multiple times in the OpenConnection method, and second, because connect is a shared variable, It is necessary to use synchronization to ensure thread safety when calling connect, 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.
Two. Deep analysis of the Threadlocal class
In the above talk about some of the understanding of threadlocal, then we look at how the specific threadlocal is implemented.
Let's look at some of the methods provided by the Threadlocal class:
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, set () to make a copy of the variable in the current thread, and remove () to remove the copy of the variable in the current thread, InitialValue () is a protected method, It is generally 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:
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:
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:
is actually a threadlocalmap, this type is an inner class of the Threadlocal class, and we continue to take a look at the implementation of Threadlocalmap:
You can see that the entry of Threadlocalmap inherits WeakReference and uses threadlocal as the key value.
Then go on to see the concrete implementation of the Setinitialvalue method:
It is easy to do the misdeeds, that is, if the map is not empty, set the key-value pairs, empty, and then create a map to see the implementation of Createmap:
At this point, most of your friends may have understood how threadlocal creates a copy of a variable for each thread:
First of all 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> ();p ublic 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> ();p ublic 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, the runtime will report a null pointer exception without first set and direct get.
However, if you change to the following code, the InitialValue method is overridden:
public class Test {threadlocal<long> longlocal = new Threadlocal<long > () {protected Long InitialValue () {return Thread.CurrentThread (). GetId ();};}; threadlocal<string> stringlocal = new threadlocal<string> () {;p rotected 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.
Three. Threadlocal's application Scenario
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 ();}
The following code is excerpted from:
http://www.iteye.com/topic/103804
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; }
Resources:
"In-depth understanding of Java virtual machines"
The idea of Java programming
http://ifeve.com/thread-management-10/
Http://www.ibm.com/developerworks/cn/java/j-threads/index3.html
http://www.iteye.com/topic/103804
http://www.iteye.com/topic/777716
http://www.iteye.com/topic/757478
http://blog.csdn.net/ghsau/article/details/15732053
http://ispring.iteye.com/blog/162982
Http://blog.csdn.net/imzoer/article/details/8262101
Http://www.blogjava.net/wumi9527/archive/2010/09/10/331654.html
http://bbs.csdn.net/topics/380049261
Java concurrency programming: deep anatomy threadlocal