ThreadLocal function, scene and principle

Posted by ven.ganeva on Mon, 07 Feb 2022 13:20:50 +0100

Dark horse programmer Java basic tutorial from simple to deep, comprehensive analysis of threadlocal_ Beep beep beep_ bilibili

ThreadLocal data structure

Memory leak - threadLocalMap key is a strong reference

Memory leak - threadLocalMap key is a weak reference

 

Manually remove entry

 ThreadLocal function, scene and principle - brief book 1 What is ThreadLocal? Java. Java is available in JDK version 1.2 Lang. ThreadLocal, ThreadLocal provides a new way to solve the concurrency problem of multithreaded programs https://www.jianshu.com/p/6fc3bba12f38

1. What is ThreadLocal?

Java. Java is available in JDK version 1.2 Lang. ThreadLocal, ThreadLocal provides a new idea to solve the concurrency problem of multithreaded programs. Using this tool class, you can write beautiful multithreaded programs very concisely.

ThreadLocal is not a Thread, but a local variable of Thread. It may be easier to understand by naming it ThreadLocalVariable.

In jdk5 0, ThreadLocal already supports generics, and the class name of this class has changed to ThreadLocal < T >. API methods have also been adjusted accordingly. The new version of API methods are void set(T value), T get() and T initialValue().

 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID)

1.1. The role of ThreadLocal?

ThreadLocal is a good idea to solve the thread safety problem. It solves the conflict of variable concurrent access by providing an independent variable copy for each thread. In many cases, ThreadLocal is simpler and more convenient than directly using synchronized synchronization mechanism to solve thread safety problems, and the resulting program has higher concurrency.

1.1.2. Application scenario of ThreadLocal?

In Java multi-threaded programming, in order to ensure the safe access of multiple threads to shared variables, synchronized is usually used to ensure that only one thread operates on shared variables at the same time. In this case, you can Class variable Put it into an object of ThreadLocal type, so that the variable has an independent copy in each thread, and there will be no phenomenon that one thread reads the variable and is modified by another thread. The most common usage scenario of ThreadLocal is to solve database connection, Session management, etc. Several scenarios are listed below.

2. ThreadLocal can also be seen in the Android source code

Here we take the Handler of andorid source code as an example. See how Handler uses ThreadLocal. The Handler must get the Looper object of the current thread, and the Looper of each thread is inconsistent. Because each thread will have a Looper object, this effect can be achieved by using ThradLocal to save and obtain the Looper of the current thread.

2.1. Looper's internal source code for storing looper and obtaining looper in ThreadLocal.

//Looper.prepare();
​
private static void prepare(boolean quitAllowed) {
 if (sThreadLocal.get() != null) {
 throw new RuntimeException("Only one Looper may be created per thread");
 }
 //Save the created Looper object to sThreadLocal.
 sThreadLocal.set(new Looper(quitAllowed));
}
​
​
//Fetch Looper object from ThreadLocal
public static @Nullable Looper myLooper() {
 return sThreadLocal.get();
}

3. Internal principle of ThreadLocal

We understand the principle of ThreadLocal from the source code. Let's see how to implement ThreadLocal.

ThreadLocal class provides several methods:

1.public T get() { }

2.public void set(T value) { }

3.public void remove() { }

4.protected T initialValue(){ }

The get() method is used to obtain the copy of the variable saved by ThreadLocal in the current thread, set() is used to set the copy of the variable in the current thread, remove() is used to remove the copy of the variable in the current thread, and initialValue() is a protected method, which is generally used to rewrite when in use. It is a delayed loading method, which will be described in detail below.

3.1. Let's first look at the implementation of the source code of the get method

 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
 Thread t = Thread.currentThread();
 ThreadLocalMap map = getMap(t);
 if (map != null) {
 ThreadLocalMap.Entry e = map.getEntry(this);
 if (e != null) {
 @SuppressWarnings("unchecked")
 T result = (T)e.value;
 return result;
 }
 }
 return setInitialValue();
}
​
/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue() {
 T value = initialValue();
 Thread t = Thread.currentThread();
 ThreadLocalMap map = getMap(t);
 if (map != null)
 map.set(this, value);
 else
 createMap(t, value);
 return value;
}

The first sentence is to get the current thread, and then get a map through the getMap(t) method. The type of map is ThreadLocalMap. Next, get the < key, value > key value pair. Note that the key value pair is passed in this instead of the current thread t. If the acquisition is successful, the value value is returned. If the map is empty, the setInitialValue method is called to return value.

See what getMap(t) does

 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
 ThreadLocalMap getMap(Thread t) {
 return t.threadLocals;
 }

In getMap, call the current Thread T and return a member variable threadLocals in the current Thread t. So let's continue to take threadlocales from the Thread class and see what the member variable threadlocales is? Continue to view the source code

 * ThreadLocalMap is a customized hash map suitable only for
 * maintaining thread local values. No operations are exported
 * outside of the ThreadLocal class. The class is package private to
 * allow declaration of fields in class Thread.  To help deal with
 * very large and long-lived usages, the hash table entries use
 * WeakReferences for keys. However, since reference queues are not
 * used, stale entries are guaranteed to be removed only when
 * the table starts running out of space.
 */
 static class ThreadLocalMap {
​
 /**
 * The entries in this hash map extend WeakReference, using
 * its main ref field as the key (which is always a
 * ThreadLocal object).  Note that null keys (i.e. entry.get()
 * == null) mean that the key is no longer referenced, so the
 * entry can be expunged from table.  Such entries are referred to
 * as "stale entries" in the code that follows.
 */
 static class Entry extends WeakReference<ThreadLocal<?>> {
 /** The value associated with this ThreadLocal. */
 Object value;
​
 Entry(ThreadLocal<?> k, Object v) {
 super(k);
 value = v;
 }
 }
​
 //Omit
 }

In fact, it is a ThreadLocalMap. This type is an internal class of ThreadLocal class. Let's continue to take a look at the implementation of ThreadLocalMap.

Let's look at the setInitialValue() method

setInitialValue() is easy to understand. If the map is not empty, set the key value pair to be empty, and then create the map. Take a look at the implementation of createMap.

 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
 void createMap(Thread t, T firstValue) {
 t.threadLocals = new ThreadLocalMap(this, firstValue);
 }

If ThreadLocal is used, set must be performed before get, otherwise null pointer exception will be reported

public class ThreadLocalExsample {

    ThreadLocal<Long> longLocal = new ThreadLocal<>();
    public void set() {
        longLocal.set(Thread.currentThread().getId());
    }
    public long getLong() {
        return longLocal.get();
    }
 public static void main(String[] args) {
        ThreadLocalExsample test = new ThreadLocalExsample();
        //Note: before set ting, get directly and report null exception
        System.out.println("-------threadLocal value-------" + test.getLong());
    }
}

Application scenario # database connection of ThreadLocal

 public Connection initialValue() {
 return DriverManager.getConnection(DB_URL);
 }
};  

public static Connection getConnection() {  
 return connectionHolder.get();
}  

Application scenario # Session management of 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;
}

Multithreaded application scenario # thread local #

 * @Author Ann works hard in summer
 * Create Date is  2019/3/21
 *
 * describe Java Medium ThreadLocal Class allows us to create variables that can only be read and written by the same thread.
 * So if a piece of code contains a ThreadLocal Variable, even if two threads execute this code at the same time,
 * They also have no access to each other's information ThreadLocal Variable.
 */
public class ThreadLocalExsample {
​
 /**
 * A MyRunnable instance is created and passed to two threads as a parameter. The two threads execute the run() method respectively,
 * And different values are saved on the ThreadLocal instance. If they do not access the ThreadLocal object and the called set() method is synchronized,
 * The second thread will overwrite the value set by the first thread. However, because they access a ThreadLocal object,
 * Therefore, neither thread can see the value saved by the other. That is, they access two different values.
 */
 public static class MyRunnable implements Runnable {
 /**
 * Instantiates a ThreadLocal object. We only need to instantiate the object once, and we don't need to know which thread instantiates it.
 * Although all threads can access this ThreadLocal instance, each thread can only access its own instance by calling ThreadLocal
 * set()Method. Even if two different threads set different values on the same ThreadLocal object,
 * They still cannot access each other's values.
 */
 private ThreadLocal threadLocal = new ThreadLocal();
 @Override
 public void run() {
 //Once a ThreadLocal variable is created, you can set a value to be saved through the following code
 threadLocal.set((int) (Math.random() * 100D));
 try {
 Thread.sleep(2000);
 } catch (InterruptedException e) {
 }
 //You can read the value saved in the ThreadLocal variable by the following method
 System.out.println("-------threadLocal value-------"+threadLocal.get());
 }
 }
​
 public static void main(String[] args) {
 MyRunnable sharedRunnableInstance = new MyRunnable();
 Thread thread1 = new Thread(sharedRunnableInstance);
 Thread thread2 = new Thread(sharedRunnableInstance);
 thread1.start();
 thread2.start();
 }
}
​
Operation results
-------threadLocal value-------38
-------threadLocal value-------88

come to conclusion

The set and get operations in ThreadLocal are the table array of the corresponding thread, so accessing the set and get of the same ThreadLocal object in different threads will not interfere with each other.

summary

There is a ThreadLocal inside each Thread The member variable threadLocals of threadlocalmap type is used to store the actual variable copy. The key value is the current ThreadLocal variable and value is the variable copy (i.e. variable of type T). Initially, in the Thread, threadlocales is empty. When the get() method or set() method is called through the ThreadLocal variable, threadlocales in the Thread class will be initialized, and the current ThreadLocal variable will be taken as the key value, and the copy variable to be saved by ThreadLocal will be taken as the value and saved to threadlocales. Then, if you want to use copy variables in the current Thread, you can find them in threadLocals through the get method.

  1. The actual copy created through ThreadLocal is stored in each thread's own threadLocals;

  2. Why is the type of threadLocals? The key of ThreadLocalMap is ThreadLocal object, because there can be multiple ThreadLocal variables in each thread, just like longLocal and stringLocal in the above code;

  3. Set must be performed before get, otherwise null pointer exception will be reported; If you want to access it normally without calling set before get, you must override the initialValue() method. Because in the above code analysis, we found that if we do not set first, that is, we cannot find the corresponding storage in the map, we will return i by calling the setInitialValue method. In the setInitialValue method, there is a statement T value = initialValue(), and by default, the initialValue method returns null.

Use and implementation principle of ThreadLocal

What is ThreadLocal?

ThreadLocal provides thread local variables. Each thread has a copy of the local variables. The variables between threads do not interfere with each other. ThreadLocal is implemented to ensure the safety of variables in a multithreaded environment. The following comments are from the ThreadLocal class.

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.

Use of ThreadLocal

The example in JDK defines a private and static ThreadLocal variable, and rewrites the initialValue method. Each thread will call this method to obtain the initialization value.

Examples in JDK

If necessary, we can override the initialValue method to implement our own business logic.

In fact, it is very simple to save the value to call the set method, get the value to call the get method, and finally call the remove method to delete the data, so as to prevent memory leakage and data confusion.

Data structure of ThreadLocal

There is a variable threadLocals in the Thread class. This type is an internal class ThreadLocalMap in ThreadLocal. This class does not implement the map interface, but it is an ordinary Java class, but it implements functions similar to map.

ThreadLocal data structure

Each thread needs its own map. Map is an array data structure that stores data. Each element is an entry. The key of the entry is the reference of threadlocal, that is, the copy of the current variable, and value is the value of set.

Source code analysis of ThreadLocal

1. There is a variable threadLocals in the Thread class. The type is ThreadLocal Threadlocalmap, which is to save the private data of each Thread.

threadLocals

2. ThreadLocalMap is the internal class of ThreadLocal. Each data is saved with an Entry. The Entry inherits from WeakReference and is stored with a key value pair. The key is the reference of ThreadLocal. Why WeakReference? If it is a strong reference, even if ThreadLocal is set to null, GC will not recycle because ThreadLocalMap has a strong reference to it.

Definition of Entry

3. The implementation logic of the set method in ThreadLocal first obtains the current thread and takes out the ThreadLocalMap of the current thread. If it does not exist, a ThreadLocalMap will be created. If it does exist, the reference of the current ThreadLocal will be used as the key and the passed in parameters will be stored in the map as the value.

set method

4. The implementation logic of the get method in ThreadLocal obtains the current thread, takes out the ThreadLocalMap of the current thread, and uses the current threadlock as the key to find it in ThreadLocalMap. If there is an Entry that is not empty, the value in the Entry will be returned. Otherwise, initialization will be performed and the default value will be returned.

get method

5. The implementation logic of the remove method in ThreadLocal is to first obtain the ThreadLocalMap variable of the current thread, and call the remove method of ThreadLocalMap if it exists. The storage of ThreadLocalMap is the implementation of the array, so you need to determine the location of the element, find the entry, set the key value pairs of the entry to null, and finally set the entry to null. In fact, there will be hash conflicts, as shown below.

remove method

Resolve hash conflicts

The hash code in ThreadLocal is very simple, which is to call the getAndAdd method of AtomicInteger. The parameter is a fixed value 0x61c88647.

As mentioned above, the structure of ThreadLocalMap is very simple. It is stored only in an array and has no linked list structure. In case of hash conflict, linear search is adopted. The so-called linear search is to determine the position of elements in the table array according to the hashcode value of the initial key. If it is found that elements with other key values have been occupied in this position, Then the fixed algorithm is used to find the next position of a certain step size, and judge in turn until the position that can be stored is found. If there are multiple hash conflicts, it will not be as efficient as HashMap. In order to avoid hash conflicts, use as few threadlocal variables as possible

last

Usage scenarios: Session management of some ORM frameworks, Session management of web system, etc

Simple summary: a ThreadLocal can only save a copy of one variable. If multiple variables are needed, multiple variables must be created; We determine that we need to perform remove after use to avoid memory leakage.

Topics: Java Back-end