Java ThreadLocal in-depth anatomy

Source: Internet
Author: User

Recently look at the Android framework layer code, see threadlocal This class, a little strange, and turned over a variety of related blog one by one read; and then studied the source, and found that their understanding of the previous reading of the blog has a different point, So decided to write an article about their own understanding, hope can play the following role:

    • Can dredge the research results, deepen their understanding
    • Can play a role in helping interested students to clear up ideas
    • Share the learning experience, communicate and learn with everyone
First, what is ThreadLocal?

ThreadLocal is the base class of the Java class Library, under Package java.lang;
The official explanation is this:

Implements a thread-local storage, that's, a variable for which each thread have its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one The thread does not affect the other threads. The implementation supports NULL values.

The General meaning is:

A thread's local storage mechanism can be implemented, and the threadlocal variable is a variable in which different threads can have different values. All threads can share the same Threadlocal object, but different threads can get different values when they are accessed, and any one thread changes it without affecting other threads. The class implementation is supported with null values (you can pass and access null values in the set and Get methods).

There are generally three features:
-Different values are obtained when accessing different threads
-Changes to it by any thread do not affect other threads
-Support NULL
The following are examples of these attributes, first defining a test class, in which we verify the three attributes mentioned above. The class definition is as follows:

Test.java
 Public  class Test{    //Definition threadlocal    Private StaticThreadlocal<string> name; Public Static void Main(string[] args)throwsexception{name =NewThreadlocal<string> ();//define Thread AThread A =NewThread () { Public void Run() {System.out.println ("Before Invoke Set,value is:"+name.get ());                Name.set ("Thread A"); System.out.println ("After invoke set, value is:"+name.get ()); }        };//define Thread BThread B =NewThread () { Public void Run() {System.out.println ("Before Invoke Set,value is:"+name.get ());                Name.set ("Thread B"); System.out.println ("After Invoke Set,value is:"+name.get ()); }        };//Not invoke set, print the value is nullSystem.out.println (Name.get ());//Invoke set to fill a valueName.set ("Thread Main");//Start thread AA.start (); A.join ();//Print The value after changed the value by thread ASystem.out.println (Name.get ());//Start thread BB.start (); B.join ();//Print The value after changed the value by thread BSystem.out.println (Name.get ())}}
Code Analysis:

From the definition we can see that only one Threadlocal object is declared, the other three threads (the main thread, thread A, and thread B) share the same object, then the values of the objects are modified in different threads and the values of the objects are accessed in different threads, and the results are viewed in the console output. Look at the results:

From the console output you can see that there are three null output, this is because the object is not assigned before the output, verify the characteristics of the support of NULL; Furthermore, it can be found that I have modified the value of the object on each thread, but not the modified value when other threads access the object. Instead, it accesses thread-local values, which also validates the other two features.

Second, the role of threadlocal

We all know that its use of the scene is mostly multithreaded programming, as to the specific role, this How to say that? I think this can only be defined by a universal statement, because the functional properties of a thing defines the future will limit everyone's thinking, like saying the chopper is used to cut vegetables, many people will not use it cut watermelon.
Here, my understanding of its role, for reference only, hope to be helpful. In this way, when a multithreaded program needs to encapsulate some of the tasks of most threads (that is, part of the code in the Run method), in the wrapper, you can use threadlocal to wrap thread-related member variables to guarantee the exclusivity of thread access. And all threads can share a single wrapper object; You can refer to the Looper in Android. Programmers who don't use code to describe problems are not good programmers;
See Code: A tool that counts the time spent on a piece of code in a thread (to illustrate a problem)

Statisticcosttime.java
//Class that statistic the cost time Public  class statisticcosttime{    //Record the StartTime    //Private threadlocal<long> startTime = new threadlocal<long> ();    Private LongStartTime;//Private threadlocal<long> costtime = new threadlocal<long> ();    Private LongCosttime;Private Statisticcosttime(){    }//singleton     Public Static FinalStatisticcosttimeshareinstance(){returnInstancefactory.instance; }Private Static  class instancefactory{        Private Static FinalStatisticcosttime instance =NewStatisticcosttime (); }//Start     Public void Start(){//Starttime.set (System. Nanotime ());StartTime = System.nanotime (); }//End     Public void End(){//Costtime.set (System. Nanotime ()-starttime.get ());Costtime = System.nanotime ()-startTime; } Public Long GetStartTime(){returnStartTime;//return Starttime.get ();} Public Long Getcosttime(){//return Costtime.get ();        returnCosttime; }

Well, the tool design is finished, and now we're going to use it to count the threads. Try it out:

Main.java
 Public  class Main{     Public Static void Main(string[] args)throwsexception{//Define the thread aThread A =NewThread () { Public void Run(){Try{//start record timeStatisticcosttime.shareinstance (). Start (); Sleep $);//Print the start time of ASystem.out.println ("A-starttime:"+statisticcosttime.shareinstance (). GetStartTime ());//End the recordStatisticcosttime.shareinstance (). end ();//Print the costtime of ASystem.out.println ("A:"+statisticcosttime.shareinstance (). Getcosttime ()); }Catch(Exception e) {                }            }        };//Start aA.start ();//Define thread BThread B =NewThread () { Public void Run(){Try{//Record the start time of B1Statisticcosttime.shareinstance (). Start (); Sleep -);//Print the Start time to consoleSystem.out.println ("B1-starttime:"+statisticcosttime.shareinstance (). GetStartTime ());//End record start time of B1Statisticcosttime.shareinstance (). end ();//Print The cost time of B1System.out.println ("B1:"+statisticcosttime.shareinstance (). Getcosttime ());//Start record time of B2Statisticcosttime.shareinstance (). Start (); Sleep -);//Print start time of B2System.out.println ("B2-starttime:"+statisticcosttime.shareinstance (). GetStartTime ());//End record time of B2Statisticcosttime.shareinstance (). end ();//Print cost time of B2System.out.println ("B2:"+statisticcosttime.shareinstance (). Getcosttime ()); }Catch(Exception e)        {                }            }        };    B.start (); }}

After running the code, the output is like this.
Note: The output accuracy is nanosecond level

See whether the result is not the same as we expected, found that the result of a should be about equal to B1+B2 only right, how to become the same as the B2 it? The answer is that when we define starttime and costtime variables, the intent is not to be shared and should be thread-exclusive. And here the variable with the singleton share, so when the value of a, actually starttime has been B2 modified, so the output and B2 the same result.
Now let's open the annotated part of the statisticcosttime and replace it with the Threadlocal declaration.
Look at the results:

Yes! This to achieve the desired effect, this time there are students say this is not possible to thread concurrent access, is not as long as I use the threadlocal can be guaranteed thread safety? The answer is no!. First of all understand why the thread security problem, nothing more than two kinds of feelings:
1, should not share the resources, you shared between the threads;
2, the resources shared between the threads, you do not guarantee orderly access;
The former can be used "space for Time" solution, with threadlocal (can also directly declare thread local variables), the latter with "time for space" way to solve, obviously this is not threadlocal can do.

Three, ThreadLocal principle

The principle of implementation is actually very simple, every time the read and write operation on the Threadlocal object is the read and write operation of the thread's values object; Here is a clarification, there is no variable copy creation, because there is no memory space allocated by the variable to save the T object, Instead, it uses the values of its thread to store the T object; When we call Threadlocal's set method in a thread, we actually write the object to the threads corresponding to the values object, and when you call Threadlocal's Get method, is actually the process of taking object from the thread corresponding to the values object.
See Source:

Member variable set for ThreadLocal
/** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */publicvoidset(T value) {    Thread currentThread = Thread.currentThread();    Values values = values(currentThread);    ifnull) {        values = initializeValues(currentThread);    }    values.put(this, value);}
Member method of Treadlocal get
/** * Returns The value of this variable for the current thread.  If an entry * doesn ' t yet exist for this variable on this thread, this method would * create an entry, populating the value With the result of * {@link #initialValue ()}. * * @return The current value of the variable for the calling thread. */@SuppressWarnings("Unchecked") PublicTGet() {//Optimized for the fast path.Thread CurrentThread = Thread.CurrentThread (); Values values = VALUES (CurrentThread);if(Values! =NULL) {object[] table = values.table;intIndex = hash & values.mask;if( This. Reference = = Table[index]) {return(T) Table[index +1]; }    }Else{values = Initializevalues (CurrentThread); }return(T) Values.getaftermiss ( This);}
Member method of Threadlocal Initializevalues
/** * Creates Values instance for this thread and variable type. */Values initializeValues(Thread current) {    returnnew Values();}
Member method of ThreadLocal values
/** * Gets Values instance for this thread and variable type. */Values values(Thread current) {    return current.localValues;}
So how does this values read and write object?

Values are present as inner classes of threadlocal, which includes an important array object[], which is a key part of the solution to the problem, which is used to store various types of treadlocal variables used by the thread; How do you ensure that you do not take other types of values when you specifically take a variable of a certain type? According to the general practice will use a map according to Key-value map; Yes, the idea is that this idea, but there is no map to achieve, is a object[] implementation of the map mechanism; however, it is not possible to use map to understand, because the mechanism is the same Key actually corresponds to Threadlocal's weak reference, and value corresponds to the object we passed in.
Explain how to use object[] to implement the map mechanism (refer to Figure 1); it is to distinguish between key and value by the odd-odd of the array subscript, that is, the following table is an even position store key, odd store value, that is, this is done; interested students if you want to know the algorithm implementation, You can study it in depth, I'm not detailing it here.

Combined with the first instance of the previous analysis, the following storage conditions:
When the program executes, there are a, B and main three threads, respectively, when calling Name.set () in the thread, and allocating three identical memory space to the heap area for the three threading instances to store the values object, with the name reference as key, The specific object is stored as a value in three different object[] (see):

Iv. Summary

ThreadLocal can not completely solve the multi-threaded programming concurrency problem, the problem also depends on different circumstances to choose a different solution, "Space Change Time" or "time for space."
Threadlocal's biggest role is to convert thread-sharing variables into thread-local variables to isolate threads.

Java ThreadLocal in-depth anatomy

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.