Implementation and usage of the ThreadLocal class
What is ThreadLocal? In fact, ThreadLocal is not a local implementation version of a Thread, it is not a Thread, but threadlocalvariable (Thread local variable ). It may be more appropriate to name it ThreadLocalVar. ThreadLocal provides a copy of the variable value for every thread that uses the variable. It is a special thread binding mechanism in Java, is that each thread can independently change its own copy, without conflict with the copies of other threads. From the thread perspective, each thread maintains an implicit reference to its local variable copy, as long as the thread is active and the ThreadLocal instance is accessible. After the thread disappears, all copies of the local instance of the thread will be garbage collected (unless there are other references to these copies ). Data accessed through ThreadLocal is always related to the current thread. That is to say, JVM binds a private local instance access space for each running thread, this provides an isolation mechanism for concurrent access problems that often occur in multi-threaded environments. How does ThreadLocal maintain copies of variables for each thread? In fact, the implementation idea is very simple. There is a Map in the ThreadLocal class, which is used to store copies of the variables of each thread. To sum up, for multi-threaded resource sharing, the synchronization mechanism adopts the "Time for space" approach, while ThreadLocal adopts the "space for Time" approach. The former provides only one copy of the variable, allowing different threads to queue for access, while the latter provides a copy of the variable for each thread. Therefore, the former can be accessed simultaneously without affecting each other.
ThreadLocal Interface Method
ThreadLocal class interface is very simple. There are only four methods. Let's take a look at it first:
- Void set (Object value) sets the value of the local variable of the current thread.
- Public Object get () This method returns the local variable of the thread corresponding to the current thread.
- Public void remove () deletes the local variable value of the current thread to reduce memory usage. This method is newly added to JDK 5.0. It should be noted that when the thread ends, the local variables of the thread will be automatically reclaimed, so explicitly calling this method to clear the local variables of the thread is not a required operation, but it can speed up memory recovery.
- Protected Object initialValue () returns the initial value of the local variable of the thread. This method is a protected method, apparently designed to overwrite the subclass. This method is a delayed call method. It is executed only once when the thread calls get () or set (Object) for 1st times. The default implementation in ThreadLocal directly returns a null value.
Code to simulate ThreadLocal implementation:
1 public class ThreadShareData { 2 static int num = 0; 3 4 5 public static void main(String[] args) { 6 Map<Thread, Integer> map = new HashMap<Thread, Integer>(); 7 for (int i = 0; i < 2; i++) { 8 new Thread(new Runnable() { 9 @Override10 public void run() {11 int num = new Random().nextInt();12 System.out.println(Thread.currentThread().getName()+":"+" get num: "+num);13 new A().get();14 new B().get();15 }16 }).start();17 }18 }19 20 static class A{21 public void get(){22 System.out.println("A: "+Thread.currentThread().getName() +"get num: "+num);23 }24 }25 26 static class B{27 public void get(){28 System.out.println("A: "+Thread.currentThread().getName() +"get num: "+num);29 }30 }31 32 }
Typical ThreadLocal usage:
1 public class ThreadLocalShareData2 { 2 static ThreadLocal<People> threadLocal = new ThreadLocal<People>(); 3 public static void main(String[] args) { 4 for (int i = 0; i < 2; i++) { 5 new Thread(new Runnable() { 6 @Override 7 public void run() { 8 int data = new Random().nextInt(); 9 People people = new People().getInstance();10 people.setName("name"+data);11 people.setAge(data);12 System.out.println(Thread.currentThread().getName()+" set name "+people.getName()+" set age "+people.getAge());13 new A().get();14 new B().get();15 }16 }).start();17 }18 }19 20 static class A{21 public void get(){22 System.out.println("A: "+Thread.currentThread().getName() +"get name "+new People().getInstance().getName()+" get age "+new People().getInstance().getAge());23 }24 }25 static class B{26 public void get(){27 System.out.println("B: "+Thread.currentThread().getName() +"get name "+new People().getInstance().getName()+" get age "+new People().getInstance().getAge());28 }29 }30 static class People{31 private People(){32 33 }34 public People getInstance(){35 People people = threadLocal.get();36 if(people == null){37 people = new People();38 threadLocal.set(people);39 }40 return people;41 }42 private int age;43 private String name;44 public int getAge() {45 return age;46 }47 public String getName() {48 return name;49 }50 public void setAge(int age) {51 this.age = age;52 }53 public void setName(String name) {54 this.name = name;55 }56 }57 }
ThreadLocal is implemented in the singleton mode, which is more object-oriented.
In the ThreadLocal class, a Map is used to store copies of variables of each thread. The key of the element in the Map is the thread object, and the value corresponds to the variable copy of the thread.
1 public class TestNum {2 // ① use an anonymous internal class to overwrite the initialValue () method of ThreadLocal and specify the initial value 3 private static ThreadLocal <Integer> seqNum = new ThreadLocal <Integer> () {4 public Integer initialValue () {5 return 0; 6} 7}; 8 9 // ② obtain the next sequence value 10 public int getNextNum () {11 seqNum. set (seqNum. get () + 1); 12 return seqNum. get (); 13} 14 15 public static void main (String [] args) {16 TestNum sn = new TestNum (); 17 // ③ three threads share the sn, generate serial numbers 18 TestClient t1 = new TestClient (sn); 19 TestClient t2 = new TestClient (sn); 20 TestClient t3 = new TestClient (sn); 21 t1.start (); 22 t2.start (); 23 t3.start (); 24} 25 26 private static class TestClient extends Thread {27 private TestNum sn; 28 29 public TestClient (TestNum sn) {30 this. sn = sn; 31} 32 33 public void run () {34 for (int I = 0; I <3; I ++) {35 // ④ each thread generates 3 Series values 36 System. out. println ("thread [" + Thread. currentThread (). getName () + "] --> sn [" 37 + sn. getNextNum () + "]"); 38} 39} 40} 41}
We found that although the serial numbers produced by each thread share the same TestNum instance, they do not interfere with each other, but they each generate independent serial numbers, this is because we provide separate copies for each thread through ThreadLocal.
Comparison of Thread synchronization mechanisms:
What are the advantages of ThreadLocal over thread synchronization? ThreadLocal and thread synchronization mechanisms are designed to solve the access conflict between the same variables in multiple threads.
In the synchronization mechanism, the object lock mechanism ensures that only one thread accesses the variable at a time. At this time, the variable is shared by multiple threads. The synchronization mechanism requires the program to carefully analyze when to read and write the variable, and when to lock an object, when to release the object lock and other complicated issues, it is relatively difficult to design and write the program.
ThreadLocal solves multi-thread concurrent access from another perspective. ThreadLocal provides an independent copy of variables for each thread, thus isolating multiple threads from access conflicts to Data. Because every thread has its own copy of the variable, there is no need to synchronize the variable. ThreadLocal provides thread-safe shared objects. When writing multi-threaded code, you can encapsulate insecure variables into ThreadLocal.
Because ThreadLocal can hold any type of objects, get () provided by JDK of lower versions returns Object objects and requires forced type conversion. However, JDK 5.0 solves this problem through generics and simplifies the use of ThreadLocal to a certain extent. In Code List 9 2, the new ThreadLocal version of JDK 5.0 is used.
To sum up, for multi-threaded resource sharing, the synchronization mechanism adopts the "Time for space" approach, while ThreadLocal adopts the "space for Time" approach. The former provides only one copy of the variable, allowing different threads to queue for access, while the latter provides a copy of the variable for each thread. Therefore, the former can be accessed simultaneously without affecting each other.
Spring uses ThreadLocal to solve thread security issues. We know that in general, only stateless beans can be shared in multi-threaded environments. In Spring, most beans can be declared as singleton scopes. This is because Spring uses ThreadLocal to process non-thread security states in some beans (such as RequestContextHolder, TransactionSynchronizationManager, and LocaleContextHolder), making them thread-safe, because stateful beans can be shared in multiple threads.
Generally, Web applications are divided into three layers: presentation layer, service layer, and persistence layer. The corresponding logic is written in different layers. The lower layer opens function calls through interfaces to the upper layer. In general, all program calls from receiving a request to returning a response belong to the same thread, as shown in figure 9:
The same thread runs through three layers so that you can store some non-thread-safe variables as ThreadLocal as needed, in the same request response call thread, all associated Objects Reference the same variable.
The following example shows how Spring modifies stateful beans:
Non-thread security code:
1 public class TestDao {2 private Connection conn; // ① A non-thread-safe variable 3 4 public void addTopic () throws SQLException {5 Statement stat = conn. createStatement (); // ② reference the non-thread security variable 6 //... 7} 8}
Thread security code:
1 public class TestDaoNew {2 // ① use ThreadLocal to save the Connection variable 3 private static ThreadLocal <Connection> connThreadLocal = new ThreadLocal <Connection> (); 4 5 public static Connection getConnection () {6 // ② If connThreadLocal does not have the Connection corresponding to this thread, create a new Connection, 7 // and save it to the local variable of the thread. 8 if (connThreadLocal. get () = null) {9 Connection conn = getConnection (); 10 connThreadLocal. set (conn); 11 return conn; 12} else {13 return connThreadLocal. get (); // ③ directly return the thread local variable 14} 15} 16 17 public void addTopic () throws SQLException {18 // ④ obtain the Connection 19 Statement stat = getConnection () corresponding to the thread from ThreadLocal (). createStatement (); 20} 21}
When different threads use TopicDao, determine connThreadLocal first. whether get () is null. If it is null, it indicates that the current thread has no corresponding Connection object. In this case, create a Connection object and add it to the local thread variable. If it is not null, it indicates that the current thread already has a Connection object and can be used directly. This ensures that different threads Use thread-related connections instead of other threads. Therefore, this TopicDao can be shared by singleton.
Of course, this example is rough. Placing the ThreadLocal of Connection directly on DAO can only achieve that thread security issues do not occur when multiple methods of the DAO share the Connection, but it cannot share the same Connection with other DAO. To share the same Connection with multiple DAO in the same transaction, ThreadLocal must be used in a common external class to save the Connection.
1 public class ConnectionManager { 2 3 private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { 4 @Override 5 protected Connection initialValue() { 6 Connection conn = null; 7 try { 8 conn = DriverManager.getConnection( 9 "jdbc:mysql://localhost:3306/test", "username", 10 "password"); 11 } catch (SQLException e) { 12 e.printStackTrace(); 13 } 14 return conn; 15 } 16 }; 17 18 public static Connection getConnection() { 19 return connectionHolder.get(); 20 } 21 22 public static void setConnection(Connection conn) { 23 connectionHolder.set(conn); 24 } 25 }
There is also a classic instance for session management in HibernateUtil:
1 public class HibernateUtil {2 private static Log log = LogFactory. getLog (HibernateUtil. class); 3 private static final SessionFactory sessionFactory; // defines SessionFactory 4 5 static {6 try {7 // use the default configuration file hibernate. cfg. xml creates SessionFactory 8 sessionFactory = new Configuration (). configure (). buildSessionFactory (); 9} catch (Throwable ex) {10 log. error ("failed to initialize SessionFactory! ", Ex); 11 throw new ExceptionInInitializerError (ex); 12} 13} 14 15 // create a thread local variable session, used to save the Session16 public static final ThreadLocal session of Hibernate = new ThreadLocal (); 17 18/** 19 * Get Session20 * @ return Session21 * @ throws HibernateException22 */23 public static Session currentSession () throws HibernateException {24 Session s = (Session) in the current thread) session. get (); 25 // if the Session has not been opened, a new Session26 if (S = null) {27 s = sessionFactory. openSession (); 28 session. set (s); // Save the new Session to the local variable 29} 30 return s; 31} 32 33 public static void closeSession () throws HibernateException {34 // gets the local variable of the thread and forcibly converts it to Session Type 35 Session s = (Session) session. get (); 36 session. set (null); 37 if (s! = Null) 38 s. close (); 39} 40}
Summary:
ThreadLocal is used to solve data inconsistency caused by concurrency in multiple threads. ThreadLocal provides a copy of the data concurrently accessed by each thread and runs the business by accessing the copy. This results in memory consumption, which greatly reduces the performance consumption caused by thread synchronization, it also reduces the complexity of thread concurrency control. ThreadLocal cannot use the atomic type, but can only use the Object type. ThreadLocal is much easier to use than synchronized. Both ThreadLocal and Synchonized are used to solve multi-thread concurrent access. However, ThreadLocal is essentially different from synchronized. Synchronized uses the lock mechanism to allow a variable or code block to be accessed by only one thread at a time. ThreadLocal provides a copy of the variable for each thread, so that each thread does not access the same object at a certain time, thus isolating multiple threads from sharing data. Synchronized, on the contrary, is used to obtain data sharing when multiple threads communicate. Synchronized is used for data sharing between threads, while ThreadLocal is used for data isolation between threads. Of course, ThreadLocal cannot replace synchronized. They process different problem domains. Synchronized is used to implement synchronization mechanism, which is more complex than ThreadLocal. Reference: JDK official documentation http://blog.csdn.net/lufeng20/article/details/24314381http://lavasoft.blog.51cto.com/62575/51926/