Java concurrent programming: in-depth analysis of ThreadLocal

Source: Internet
Author: User

Java concurrent programming: in-depth analysis of ThreadLocal
Many of my friends are familiar with ThreadLocal. Today we will discuss how to use and implement ThreadLocal. First, this article first talks about the understanding of ThreadLocal, then analyzes its implementation principle and precautions based on the source code of the ThreadLocal class, and finally provides two application scenarios. The following is the outline of the Directory in this article: 1. Understanding ThreadLocal 2. In-depth analysis of ThreadLocal Class 3. Understanding the application scenarios of ThreadLocal if any errors occur. You are welcome to criticize and correct them. 1. ThreadLocal: ThreadLocal is called a local thread variable in many places, and is also called a local thread storage in some places. Many may know that ThreadLocal creates a copy of the variable in each thread, so each thread can access its own internal copy variable. This sentence is literally easy to understand, but it is not that easy to understand. Let's take an example: 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 such a database link management class. This code is used in a single thread, but what if it is used in multiple threads? Obviously, thread security issues exist in multithreading: first, the two methods are not synchronized, and it is likely that the openConnection method will create connect multiple times; second, because connect is a shared variable, synchronization must be used in the place where connect is called to ensure thread security, because a thread may be using connect for database operations, another thread calls closeConnection to close the link. For the sake of thread security, the two methods of this Code must be processed synchronously, and synchronous processing must be performed at the place where connect is called. This will greatly affect the program execution efficiency, because when a thread uses connect for database operations, other threads only have to wait. So let's take a closer look at this question. Do you need to share the connect variable? In fact, it is not required. If there is a connect variable in each thread, the access to the connect variable between threads is actually not dependent, that is, a thread does not need to worry about whether other threads have modified the connect. At this point, some may think that since this variable does not need to be shared between threads, you can directly process it like this, and create a database link only when each method that requires database connection is used, then release the connection after the method call is completed. For example, 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. there is no problem with closeConnection () ;}}. Because each connection is created inside the method, there is no thread security issue between threads. However, this will have a fatal impact: as a result, the pressure on the server is very high and the program execution performance is seriously affected. Because the database connection needs to be enabled and closed frequently in the method, this will not seriously affect the program execution efficiency, but may also cause huge pressure on the server. In this case, ThreadLocal is not suitable, because ThreadLocal creates a copy of the variable in each thread, that is, each thread has a variable, it can be used anywhere inside the thread without affecting each other. As a result, there is no thread security problem and the program execution performance will not be seriously affected. However, it should be noted that although ThreadLocal can solve the problem mentioned above, because a copy is created in each thread, we should consider its resource consumption, for example, memory usage is larger than ThreadLocal usage. Ii. In-depth analysis of the ThreadLocal class I talked about some understanding of ThreadLocal. Let's take a look at how ThreadLocal is implemented. First, let's take a look at several methods provided by the ThreadLocal class: public T get () {} public void set (T value) {} public void remove () {} protected T initialValue () {} get () is used to obtain a copy of the variables stored in the current thread by ThreadLocal, set () is used to set a copy of the variables in the current thread, remove () it is used to remove copies of variables in the current thread. initialValue () is a protected method, which is generally used for rewriting during use. It is a delayed loading method, which will be described in detail below. First, let's take a look at how the ThreadLocal class creates a copy of the variable for each thread. First, let's take a look at the implementation of the get method. The first sentence is to get the current thread, and then get a map through the getMap (t) method. The map type is ThreadLocalMap. Then, the <key, value> key-value pair is obtained below. Note that the key-value Pair obtained here is passed in this instead of the current thread t. If the result is obtained successfully, the value is returned. If map is empty, the setInitialValue method is called to return value. Let's take a closer look at each of the above statements: first, let's take a look at what is done in the getMap method: What we don't think is that in getMap is to call the current thread t, returns a member variable threadLocals in the current thread t. Let's continue to take the Thread class and check what the member variable threadLocals is: it is actually a ThreadLocalMap, which is an internal class of the ThreadLocal class. Let's continue to look at the implementation of ThreadLocalMap: the Entry of ThreadLocalMap inherits WeakReference and uses ThreadLocal as the key value. Next, let's continue to look at the specific implementation of the setInitialValue method: It's easy to be inferior. If map is not empty, set the key-value pair, empty, and create a Map. Let's take a look at the implementation of createMap: at this point, most of my friends may have understood how ThreadLocal creates a copy of the variable for each Thread: first, there is a ThreadLocal in each Thread. threadLocals is a member variable of the ThreadLocalMap type. This threadLocals is used to store the actual copy of the variable. The key value is the current ThreadLocal variable, and the value is the copy of the variable (that is, the T type variable ). Initially, threadLocals is empty in the Thread. When the get () method or set () method is called through the ThreadLocal variable, threadLocals In the Thread class will be initialized, take the current ThreadLocal variable as the key value and the copy variable to be saved in ThreadLocal as the value and store it in threadLocals. Then, in the current thread, if you want to use the copy variable, you can use the get method to find it in threadLocals. The following example demonstrates that ThreadLocal can be used to create 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 result of this Code is: the output result of this Code shows that in the main thread and thread1 thread, the copy value saved by longLocal is different from that saved by stringLocal. The last re-printing of the copy value in the main thread is to prove that the copy value in the main thread is indeed different from that in the thread1 thread. Summary: 1) the actual copies created through ThreadLocal are stored in the threadLocals of each thread; 2) Why is the key value of ThreadLocalMap of threadLocals type ThreadLocalMap A ThreadLocal object, because each thread can have multiple threadLocal variables, just like longLocal and stringLocal in the code above; 3) set before get, otherwise a null pointer exception is reported; to enable normal access without calling set before get, you must override the initialValue () method. In the code analysis process above, we found that if the set parameter is not set, that is, the corresponding storage cannot be found in the map, the setInitialValue method will be called to return the I, in the setInitialValue method, one statement is T value = initialValue (). By default, the initialValue method returns null. Take 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 (Strin G [] args) throws InterruptedException {final 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 ma In the thread, if you do not set it first and get it directly, a null pointer exception will be reported during running. If the following code is changed, the initialValue method is rewritten: public class Test {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 (); strin GLocal. 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 directly call get without first set. 3. The most common application scenario of ThreadLocal is to solve database connection and Session management. For example, 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 taken from: 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 ;}

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.