Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of ThreadLocal in Java

2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces the example analysis of ThreadLocal in Java, which has a certain reference value, and interested friends can refer to it. I hope you will gain a lot after reading this article.

A brief introduction to ThreadLocal

The concurrency problem is easy to occur when multiple threads access the same shared variable, especially when multiple threads write to a variable, in order to ensure thread safety, general users need to take additional synchronization measures to ensure thread safety when accessing shared variables. ThreadLocal is a way to avoid thread unsafety in multi-thread access in addition to locking. When we create a variable, if each thread accesses its own variables, then there will be no thread unsafety problems.

ThreadLocal is provided by the JDK package, which provides thread-local variables. If you create a happy ThreadLocal variable, each thread accessing this variable will have a copy of this variable. In the actual multithreaded operation, it operates on its own local memory variables, thus avoiding thread safety issues, as shown in the following figure.

2. Simple use of ThreadLocal

In the following example, two threads are started, the value of the local variable is set within each thread, and then the print method is called to print the value of the current local variable. If calling the remove method of a local variable after printing deletes the variable in local memory, the code is as follows

1 package test; 2 3 public class ThreadLocalTest {4 5 static ThreadLocal localVar = new ThreadLocal (); 6 7 static void print (String str) {8 / / print the value of local variables in local memory in the current thread 9 System.out.println (str + ":" + localVar.get ()); 10 / clear local variables in local memory 11 localVar.remove () 12} 13 14 public static void main (String [] args) {15 Thread T1 = new Thread (new Runnable () {16 @ Override17 public void run () {18 / / set the value of the local variable in thread 1 to 19 localVar.set ("localVar1") 20 / / call print method 21 print ("thread1"); 22 / print local variable 23 System.out.println ("after remove:" + localVar.get (); 24} 25}) 26 27 Thread T2 = new Thread (new Runnable () {28 @ Override29 public void run () {30 / / set the value of the local variable in thread 1 31 localVar.set ("localVar2"); 32 / / call the printing method 33 print ("thread2") 34 / / print local variable 35 System.out.println ("after remove:" + localVar.get (); 36} 37}); 38 39 t1.start (); 40 t2.start (); 41} 42}

The following is the result of the run:

Third, the realization principle of ThreadLocal

The following is the class diagram structure of ThreadLocal

You can see from the figure that there are two variables threadLocals and inheritableThreadLocals in the Thread class, both of which are variables of the ThreadLocalMap type of the ThreadLocal inner class. By looking at the inner ThreadLocalMap, we can find that it is actually similar to a HashMap. By default, both variables in each thread are null, and they are created only when the thread calls the set or get methods of ThreadLocal for the first time (we'll look at the source code for both methods later). In addition, unlike what I thought, the local variable for each thread is not stored in the ThreadLocal instance, but in the calling thread's ThreadLocals variable (which, as mentioned earlier, is a variable of the Thread class). In other words, the local variable of ThreadLocal type is stored in the specific thread space, which is equivalent to a tool shell loaded with local variable. The value is added to the threadLocals of the calling thread through the set method, and the variable can be extracted from its threadLocals when the calling thread calls the get method. If the calling thread does not terminate, the local variable will always be stored in its threadLocals, so if you do not use the local variable, you need to call the remove method to delete the unused local variable in the threadLocals. Let's take a look at the set, get and remove methods of ThreadLocal to see exactly how ThreadLocal works

1. Set method source code 1 public void set (T value) {2 / / (1) get the current thread (caller thread) 3 Thread t = Thread.currentThread (); 4 / / (2) use the current thread as the key value to find the corresponding thread variable and find the corresponding map 5 ThreadLocalMap map = getMap (t) 6 / / (3) if map is not null, add the local variable directly. Key is the this reference of the currently defined ThreadLocal variable with the value of the added local variable 7 if (map! = null) 8 map.set (this, value); 9 / / (4) if map is null, you need to first create the corresponding map10 else11 createMap (t, value); 12}

In the above code, the getMap method is called at (2) to get the threadLocals corresponding to the current thread (see the illustration and text description above). The code for this method is as follows

ThreadLocalMap getMap (Thread t) {return t.threadLocals; / / get the thread's own variable threadLocals and bind to the current calling thread's member variable threadLocals}

If the value returned by calling the getMap method is not null, the value is directly set to the threadLocals (key is the current thread reference and the value is the local variable). If the getMap method returns null, it is the first time to call the set method (as mentioned earlier, the thread default value is null, and map is only created when the thread method is called), then you need to call the createMap method to create threadLocals, as shown below

1 void createMap (Thread t, T firstValue) {2 t.threadLocals = new ThreadLocalMap (this, firstValue); 3}

The createMap method not only creates the threadLocals, but also adds the value of the local variable to be added to threadLocals.

2. Source code of get method

In the implementation of the get method, first get the current caller thread, if the current thread's threadLocals is not null, directly return the local variable value bound by the current thread, otherwise execute the setInitialValue method to initialize the threadLocals variable. In the setInitialValue method, the implementation similar to the set method determines whether the threadLocals variable of the current thread is null, and adds the local variable (the added value is null because it is initialized at this time), otherwise the threadLocals variable is created, and the added value is null.

1 public T get () {2 / / (1) get the current thread 3 Thread t = Thread.currentThread (); 4 / / (2) get the threadLocals variable 5 ThreadLocalMap map = getMap (t) of the current thread; 6 / / (3) if the threadLocals variable is not null, you can find the value of the local variable 7 if (map! = null) {8 ThreadLocalMap.Entry e = map.getEntry (this) in map 9 if (e! = null) {10 @ SuppressWarnings ("unchecked") 11 T result = (T) e.valuetion 12 return result;13} 14} 15 / / (4) this is where threadLocals is null, calling the threadLocals variable 16 return setInitialValue () that initializes the current thread with the change 17} 18 19 private T setInitialValue () {20 / / protected T initialValue () {return null;} 21 T value = initialValue (); 22 / get the current thread 23 Thread t = Thread.currentThread (); 24 / / use the current thread as the key value to find the corresponding thread variable and find the corresponding map25 ThreadLocalMap map = getMap (t) 26 / / if map is not null, add the local variable directly. Key is the current thread, and the value is 27 if (map! = null) 28 map.set (this, value); 29 / / if map is null, the corresponding map30 else31 createMap (t, value) needs to be created first; 32 return value;33} 3, the implementation of remove method

The remove method determines whether the threadLocals variable corresponding to the current thread is null, and deletes the specified threadLocals variable in the current thread directly without null.

1 public void remove () {2 / get the threadLocals3 ThreadLocalMap m = getMap (Thread.currentThread ()) bound by the current thread; 4 / / if map is not null, remove the local variable 5 if (m! = null) 6 m.remove (this) of the specified ThreadLocal instance in the current thread; 7}

As shown in the following figure: inside each thread, there is a member variable named threadLocals, which is of type ThreadLocal.ThreadLocalMap (similar to a HashMap), where key is the this reference of the currently defined ThreadLocal variable and value is the value we set using the set method. Each thread's local variables are stored in its own local memory variable threadLocals, and if the current thread does not die, these local variables will always exist (so it may cause a memory overflow), so they need to be remove out after use.

4. ThreadLocal does not support inheritance

After the same ThreadLocal variable is set in the parent thread, it is not available in the child thread. (threadLocals is the local variable corresponding to the current calling thread, so naturally the two cannot be shared.)

1 package test; 2 3 public class ThreadLocalTest2 {4 5 / / (1) create the ThreadLocal variable 6 public static ThreadLocal threadLocal = new ThreadLocal (); 7 8 public static void main (String [] args) {9 / / add the main thread's local variable 10 threadLocal.set ("mainVal") to the main thread 11 / / create a new child thread 12 Thread thread = new Thread (new Runnable () {13 @ Override14 public void run () {15 System.out.println ("Local variable value in the child thread:" + threadLocal.get (); 16} 17}); 18 thread.start () 19 / / output the local variable value in the main thread 20 System.out.println ("the local variable value in the mainx thread:" + threadLocal.get ()); 21} 22} V. InheritableThreadLocal class

The ThreadLocal class mentioned above cannot provide local variables for the child thread to access the parent thread, while the InheritableThreadLocal class can do so. Here is the source code for the class.

1 public class InheritableThreadLocal extends ThreadLocal {2 3 protected T childValue (T parentValue) {4 return parentValue; 5} 6 7 ThreadLocalMap getMap (Thread t) {8 return t.ThreadLocals; 9} 10 11 void createMap (Thread t, T firstValue) {12 t.inheritableThreadLocals = new ThreadLocalMap (this, firstValue); 13} 14}

As you can see from the above code, the InheritableThreadLocal class inherits the ThreadLocal class and overrides the childValue, getMap, and createMap methods. Where the createMap method is called (when the current thread calls the set method with a map of null), the inheritableThreadLocal is created instead of the threadLocals. Similarly, the getMap method returns inheritableThreadLocal instead of threadLocals when the current caller thread calls the get method.

Let's look at when the overridden childValue method is executed and how to let the child thread access the local variable value of the parent thread. Let's start with the Thread class.

1 private void init (ThreadGroup g, Runnable target, String name, 2 long stackSize) {3 init (g, target, name, stackSize, null, true) 4} 5 private void init (ThreadGroup g, Runnable target, String name, 6 long stackSize, AccessControlContext acc, 7 boolean inheritThreadLocals) {8 / / judge the validity of the name 9 if (name = = null) {10 throw new NullPointerException ("name cannot be null"); 11} 12 13 this.name = name;14 / / (1) get the current thread (parent thread) 15 Thread parent = currentThread () 16 / / Security check 17 SecurityManager security = System.getSecurityManager (); 18 if (g = = null) {/ / g: current thread group 19 if (security! = null) {20 g = security.getThreadGroup (); 21} 22 if (g = = null) {23 g = parent.getThreadGroup (); 24} 25} 26 g.checkAccess () 27 if (security! = null) {28 if (isCCLOverridden (getClass () {29 security.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION); 30} 31} 32 33 g.addUnstarted (); 34 35 this.group = g; / / set to the current thread group 36 this.daemon = parent.isDaemon (); / / daemon thread or not (same as parent thread) 37 this.priority = parent.getPriority () / / priority same parent thread 38 if (security = = null | | isCCLOverridden (parent.getClass () 39 this.contextClassLoader = parent.getContextClassLoader (); 40 else41 this.contextClassLoader = parent.contextClassLoader;42 this.inheritedAccessControlContext = 43 acc! = null? Acc: AccessController.getContext (); 44 this.target = target;45 setPriority (priority); 46 / / (2) if the inheritableThreadLocal of the parent thread is not null47 if (inheritThreadLocals & & parent.inheritableThreadLocals! = null) 48 / / (3) set the inheritableThreadLocals in the child thread as the inheritableThreadLocals49 this.inheritableThreadLocals of the parent thread = 50 ThreadLocal.createInheritedMap (parent.inheritableThreadLocals); 51 this.stackSize = stackSize;52 53 tid = nextThreadID (); 54}

In the init method, you first get the current thread (parent thread) at (1), then determine whether the inheritableThreadLocals of the current parent thread is null at (2), then call createInheritedMap to create a new ThreadLocalMap variable with the inheritableThreadLocals of the parent thread as the constructor parameter, and then assign it to the child thread. Here is the createInheritedMap method and the construction method of ThreadLocalMap

1 static ThreadLocalMap createInheritedMap (ThreadLocalMap parentMap) {2 return new ThreadLocalMap (parentMap); 3} 4 5 private ThreadLocalMap (ThreadLocalMap parentMap) {6 Entry [] parentTable = parentMap.table; 7 int len = parentTable.length; 8 setThreshold (len); 9 table = new Entry [len]; 10 11 for (int j = 0; j < len; jacks +) {12 Entry e = parentTable [j] 13 if (e! = null) {14 @ SuppressWarnings ("unchecked") 15 ThreadLocal key = (ThreadLocal) e.get (); 16 if (key! = null) {17 / / call the rewritten method 18 Object value = key.childValue (e.value); 19 Entry c = new Entry (key, value) 20 int h = key.threadLocalHashCode & (len-1); 21 while (table [h]! = null) 22 h = nextIndex (h, len); 23 table [h] = c Ting 24 size++;25} 26} 27} 28}

Assign the value of the parent thread's inheritableThreadLocals member variable to the new ThreadLocalMap object in the constructor. Returns the inheritableThreadLocals that is then assigned to the child thread. In short, the InheritableThreadLocals class saves the local variable to the inheritableThreadLocals variable of the specific thread by overriding the getMap and createMap methods, and when the thread sets the variable through the set or get method of the InheritableThreadLocals instance, the inheritableThreadLocals variable of the current thread is created. When the parent thread creates the child thread, the constructor in ThreadLocalMap copies a copy of the variable from the parent thread's inheritableThreadLocals to the child thread's inheritableThreadLocals variable.

Sixth, from the perspective of ThreadLocalMap, the problem of memory leakage caused by improper use of ThreadLocal 1. Basic concepts

First of all, let's take a look at the class diagram of ThreadLocalMap. In the previous introduction, we know that ThreadLocal is just a tool class. It provides users with get, set, and remove interfaces to operate threadLocals (member variables of the calling thread) that actually store local variables. We also know that threadLocals is a variable of type ThreadLocalMap. Let's take a look at ThreadLocalMap. Before that, let's recall the four reference types in Java, and the relevant GC is just a reference to the previous series of articles (JVM related)

① strong reference: the default reference type in Java. If an object has a strong reference, it will not be GC as long as the reference exists.

② soft reference: in short, if an object has a weak reference, it will not GC the object before OOM occurs in JVM (that is, enough memory); it will not be dropped until JVM runs out of memory. A soft reference is used in conjunction with a reference queue, and if the object referenced by the soft reference is recycled, the reference will be added to the reference queue associated with it

③ weak reference (the focus of the Entry class in ThreadLocalMap is discussed here): if an object only has a weak reference, it will be dropped by the garbage collector GC (the object referenced by the weak reference can only survive until the next GC, and when GC occurs, the object referenced by the weak reference will be recycled regardless of whether the current memory is sufficient or not). A weak reference is also used in conjunction with a reference queue, and if the weakly referenced object is recycled during the garbage collection period, JVM will add the reference to the reference queue associated with it. If the referenced object can be obtained through the weakly referenced get method, when the referenced object is recycled, calling the get method will return null

④ virtual reference: a virtual reference is the weakest of all references and exists to notify the associated virtual reference object after it has been dropped by GC. (the object it points to cannot be obtained through the get method)

2. Analyze the internal implementation of ThreadLocalMap.

Above we know that the interior of ThreadLocalMap is actually an array of Entry. Let's first look at the inner class of Entry.

1 / * * 2 * is a class inherited from WeakReference. The actual key stored in this class is 3 * a weak reference to ThreadLocal and its corresponding value (the value 4 * is the value passed through ThreadLocal's set method) 5 * because it is a weak reference, when the get method returns null, it means that the pit can reference 6 * / 7 static class Entry extends WeakReference

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report