Java concurrency programming: deep anatomy threadlocal

Source: Internet
Author: User
Tags thread class

First, the understanding of 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.

Threadlocal's official API is interpreted as:

"This class provides thread-local (thread-local) variables. These variables are different from their ordinary counterparts, because each thread that accesses a variable (through its Get or set method) has its own local variable, independent of the initialized copy of the variable. ThreadLocal instances are typically private static fields in a class that want to associate the state with a thread (for example, a user ID or transaction ID). "

The approximate meaning is two points:

1. Threadlocal provides a special way to access a variable: The variable that is accessed belongs to the current thread, that is, the variable is guaranteed for each thread, and the variable that the same thread gets at any place is consistent, which is called thread isolation.

2, if you want to use threadlocal, usually defined as the private static type, in my opinion it is best to define the private static final type.

Many bloggers say this: Threadlocal provides a new way to solve the concurrency problem of multi-thread programming; Threadlocal is designed to solve the problem of sharing multiple threads accessing resources. If you think so too, now give you 10 seconds to clear the threadlocal of the mistake before!

Threadlocal can be summed up inone sentence: The role of threadlocal is to provide local variables within threads, which function in the life cycle of a thread, reducing the complexity of the passing of some common variables within the same thread, or between several functions or components.

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.

Second, in-depth analysis of 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, The initial value of the current thread used to return this thread's local variables, which is typically overridden when used, is a lazy load 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:

1 public T get () {2     //1. First gets the current thread 3 thread     T = Thread.CurrentThread (); 4     //2. Gets the thread's Map object 5     threadlocalmap m AP = Getmap (t); 6     //3. If map is not empty, take the threadlocal instance as key to get to the corresponding entry, and then remove the object from the entry. 7     if (map! = null) {8         threadlocalmap.entry e = Map.getentry (this); 9         if (E! = null)             -return (T) e.value;1 1     }12     //If the map is empty, that is, the first time you do not call the set direct get (or call the set, and then call Remove), set the initial value of     return Setinitialvalue (); 14}

The 3rd line is to get the current thread and then get to a map,map type of threadlocalmap through the 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.

Let's analyze each of the above sentences carefully. 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 values pertaining to this thread. This map was maintained * by the ThreadLocal class. */threadlocal.threadlocalmap threadlocals = null;

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.

To summarize, the 3rd and 5th lines of the Get () method are obviously getting the threadlocalmap that belong to the current thread, and if the map is not empty, we will use the current threadlocal as the key to get the corresponding entry. Entry is a static inner class of Threadlocalmap that inherits from a weak reference, so calling the E.value method in the Get () method can get the actual resource copy value, as in line 10th. However, if there is a null, stating that a copy of the resource that belongs to the thread does not yet exist, you need to create a copy of the resource, which you can see from the code is the call to the Setinitialvalue () method, which is defined as follows:

1/** 2  * Variant of Set () to establish initialvalue. Used instead 3  * of Set () in case user has overridden the set () method. 4  * 5  * @return The initial value 6
   */7 Private T Setinitialvalue () {8     t value = InitialValue (); 9     Thread T = Thread.CurrentThread ();     Localmap map = getmap (t); one     if (map! = null)         Map.set (this, value);     else14         createmap (t, value); 15     return value;16}

Line 8th invokes the InitialValue () method to initialize a value. The next step is to determine if the thread's Threadlocalmap is empty, to set the value directly (the key is this, value to be values), and to create a map with null, and the call method is Createmap (), which is defined as follows:

1 void Createmap (Thread T, T firstvalue) {2     t.threadlocals = new Threadlocalmap (this, firstvalue); 3}

Simple and straightforward, the implementation of this construction method of Threadlocalmap is as follows:

/** * Construct A new map initially containing (Firstkey, firstvalue). * Threadlocalmaps is constructed lazily, so we had create * one when we had at least one entry to put in it. */threadlocalmap (ThreadLocal Firstkey, Object firstvalue) {    table = new Entry[initial_capacity];    int i = Firstkey.threadlocalhashcode & (initial_capacity-1);    Table[i] = new Entry (Firstkey, firstvalue);    size = 1;    Setthreshold (initial_capacity);}

Instantiates a table array for storing key-value pairs, and then mapping the key-value pairs to the appropriate location.

Let's look at the set method below.

/**  * Sets the current thread's copy of this thread-local variable * to the  specified value.  Most subclasses'll has a no need  to * override this method, and relying solely on the {@link #initialValue}  * method To set the values of Thread-locals.  *  * @param value The value of the stored in the current thread's copy of * this        thread-local.  *  /public void Set (T value) {      //Gets the current thread object thread      T = Thread.CurrentThread ();      Gets the current thread local variable map      threadlocalmap map = getmap (t);      Map is not empty      if (map! = NULL)          //stored value          map.set (this, value);      else          //Create a current thread local variable map          createmap (t, value);  }

Inside this method we see that we first get a threadlocalmap related to the current thread through the Getmap (thread T) method, and then set the value of the variable to the Threadlocalmap object, Of course, if the obtained Threadlocalmap object is empty, it is created by the Createmap method.

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.

Iii. examples

Example 1:

Here's an example of how the threadlocal can achieve the effect of creating a copy of a variable in each thread:

Package Com.demo.test;public class Testthreadlocal {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 testthreadlocal test = new Tes                 Tthreadlocal ();        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:

1main8thread-01main

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 a get is made, it must be set, otherwise the null pointer exception will be reported. 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.

Note: InitValue (), by default, returns NULL. The Get method calls the InitValue method by default when the first call to get is called before the set is called. So if this method is not covered, it may cause the get to return null. Of course, this is not the case if you call a set. But often in multi-threaded situations we cannot guarantee that each thread calls the set before calling get, so it is best to overwrite the initvalue so as not to cause a null pointer exception.

Look at the following example:

Package Com.demo.test;public class Testthreadlocal {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 testthreadlocal test = new Tes                 Tthreadlocal ();        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 ()); }}

In the main thread, the runtime will report a null pointer exception without first set and direct get.

Exception in thread "main" java.lang.NullPointerException at    Com.demo.test.TestThreadLocal.getLong ( testthreadlocal.java:14) at    Com.demo.test.TestThreadLocal.main (testthreadlocal.java:25)

However, if you change to the following code, the InitialValue method is overridden:

Package Com.demo.test;public class Testthreadlocal {threadlocal<long> longlocal = new Threadlocal<long> () {         Protected Long InitialValue () {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 testthreadlocal test = new Tes                 Tthreadlocal ();        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.

Example 2:

Package Com.demo.test;public class Testnum {//① the InitialValue () method that overrides threadlocal by an anonymous inner class, specifying the initial value private static Thread local<integer> seqNum = new threadlocal<integer> () {public Integer InitialValue () {RET          Urn 0;        }      };          ② gets the next sequence value public int getnextnum () {Seqnum.set (Seqnum.get () + 1);      return Seqnum.get ();          } public static void Main (string[] args) {testnum sn = new Testnum ();          ③ 3 threads share sn, each generating serial number testclient T1 = new TestClient (SN);          TestClient t2 = new TestClient (SN);          testclient t3 = new TestClient (SN);          T1.start ();          T2.start ();      T3.start ();            } private static Class TestClient extends Thread {private Testnum sn;          Public testclient (Testnum sn) {this.sn = SN;       public void Run () {for (int i = 0; i < 3; i++) {//④ 3 sequence values per thread)           System.out.println ("thread[" + thread.currentthread (). GetName () + "]--sn[" + SN              . Getnextnum () + "]"); }          }      }  }

In general, we define the subclass of threadlocal by means of an anonymous inner class, providing the initial variable value, as shown in the example ①. TestClient threads produce a set of serial numbers, and at ③ we generate 3 testclient that share the same testnum instance. Run the above code and output the following results on the console:

THREAD[THREAD-0]--sn[1]thread[thread-1]--sn[1]thread[thread-2]--sn[1]thread[thread-1]--sn[2] THREAD[THREAD-0]--sn[2]thread[thread-1]--sn[3]thread[thread-2]--sn[2]thread[thread-0]--sn[3] Thread[thread-2]-sn[3]

Examining the results of the output, we find that each thread produces an ordinal number that shares the same Testnum instance, but they do not interfere with each other, but instead produce separate serial numbers, because we provide a separate copy of each thread through threadlocal.

Iv. Application Scenarios for threadlocal

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 ();}
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;}

Threadlocal objects are typically used to prevent the sharing of mutable single-instance variables or global variables .

When a static member variable is used in a class, be sure to ask yourself, does this static member variable need to be thread-safe? In other words, do multiple threads need to enjoy their own static member variables? If you need to think about it, you might want to use threadlocal.

Threadlocal's main application scenario is access to objects of multi-threaded multiple instances (one instance per thread), and this object is used in many places. For example: The same website login user, each user server will open a thread for it, each thread creates a threadlocal, inside the user basic information, etc., in many pages to jump, will display user information or get some information of the user, etc. frequent operation, So there is no contact between the multiple threads and the current thread can get the desired data in time.

Threadlocal commonly used to share data, when you want to use a variable in multiple methods, this variable is the state of the current thread, the other threads do not rely on this variable, the first thing you think of is to define the variables within the method, and then pass parameters between the methods to use, this method can solve the problem, But the annoying thing is that every method needs to declare formal parameters, declare multiple places, and call multiple places. affect the aesthetics and maintenance of the code. Is there a way to access variables like private static? This can be used anywhere in the class. This time threadlocal.

V. Summary

1, ThreadLocal The realization of the idea, we have said in the previous, each thread maintains a THREADLOCALMAP mapping table, the Mapping table key is the ThreadLocal instance itself, value is the copy variable to be stored. The ThreadLocal instance itself does not store a value, it simply provides a key that finds the replica value in the current thread. As shown in the following:

2, the secret of thread isolation lies in Threadlocalmap this class. Threadlocalmap is a static inner class of the Threadlocal class that implements the setting and acquisition of key-value pairs (as compared to the map object), each of which has a separate threadlocalmap copy that stores values that can only be read and modified by the current thread. The Threadlocal class implements the isolation of variable access in different threads by manipulating a threadlocalmap copy of each line thread. Because the variables are unique to each thread, there is no concurrency error at all. Another thing is that the key in the Threadlocalmap stored key-value pair is the Threadlocal object that the this object points to, and the value is the object you set.

3, Threadlocalmap is not to solve the thread safety problem, but provides a mechanism to bind the instance to the current thread, similar to the effect of isolation, in fact, in the method of the new variable can achieve similar effect. Threadlocalmap and thread security basically do not take sides, bound instances are not multithreaded common, but each thread new, this instance is certainly not common, if shared, it will lead to thread safety issues. The greatest use of threadlocalmap is to share the instance variable as a global variable, which can be accessed in any method of the program. Many people on the internet say Threadlocalmap is the solution to the thread safety problem, in fact, is words too literally, the two are not similar problems.

4. Threadlocal design is designed to solve the problem of resource sharing in multi-threaded programming. To mention this, we generally think of synchronized,synchronized to take the "time for Space" strategy, in essence, the key resources locked, let everyone queued operation. Instead, Threadlocal takes a "space-for-time" approach, providing a separate copy of the variable for each thread that uses the variable, which, within this thread, is equivalent to a "global variable" that guarantees that the thread will manipulate the same object at any time.

5, Threadlocal class The most important concept is that its principle is through a threadlocal static inner class Threadlocalmap implementation, but in practice, threadlocal do not save Threadlocalmap, Instead, there is a data structure maintained within each thread ThreadLocal.ThreadLocalMap threadLocals .

Drawing a picture here is easier to understand if we have the following code:

Class threadlocaldemo{    threadlocal<integer> Locala = new threadlocal<integer> ();    threadlocal<integer> localb = new threadlocal<integer> ();}

In a multithreaded environment, the data structure should be as shown:

6, threadlocal use of the general steps:

(1) in a multithreaded class (such as the Threaddemo Class), create a Threadlocal object threadxxx, which is used to hold the object XXX between threads that need to be treated in isolation.

(2) in the Threaddemo class, create a Method getxxx () that gets the data to be quarantined, and in the method it is determined that if the Threadlocal object is null, a new () object should be created to isolate the access type and cast to the type to be applied.

(3) in the Run () method of the Threaddemo class, the data to be manipulated is obtained by means of the getxxx () method, which ensures that each thread corresponds to a data object, which is manipulated at any time.

7. Comparison of ThreadLocal and synchronized

(1) threadlocal and synchonized are used to solve multi-threaded concurrent access. But there is an essential difference between threadlocal and synchronized. Synchronized is the mechanism by which a lock is used so that a variable or block of code can be accessed by only one thread at a time. Instead, Threadlocal provides a copy of the variable for each thread, so that each thread accesses the same object at a certain time, isolating data sharing from multiple threads. Synchronized, in contrast, is used to gain data sharing when communicating between multiple threads.

(2) Synchronized is used for data sharing between threads, while threadlocal is used for data isolation between threads.

8, a sentence to understand threadlocal: to threadlocal inside the store is to it inside the map to store things, and then threadlocal this map to hang under the current thread, so that map belongs to this thread.

Java concurrency programming: deep anatomy threadlocal

Related Article

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.