A ThreadLocal is introduced

ThreadLocal is used frequently in everyday work, but it is mostly used in a single scenario, and memory leaks can occur in the wrong cases.

So what exactly is a ThreadLocal? Let’s first look at official class annotations.

/**
 * This class provides thread-local variables.  These variables differ from
 * 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).
 * 
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 */  	
Copy the code

ThreadLocal provides local variables that can be manipulated by each thread through the set() and get() methods without conflicting with local variables of other threads, ensuring an isolated line of local variables between threads.

ThrealLocal: A variable of type ThrealLocal belongs to the current thread. Even a ThreadLocal variable within the same method will have different values for different businesses, thread-safe.

Ii.ThreadLocal service application

2.1. Maintain database connections

In the JDBC era, the database connection needs to be maintained manually, and the same library connection can be realized with the database connection pool. But when different databases connect, different threads need to get different connections. In this case, you can use ThreadLocal to maintain connections to different data sources in the thread pool.

Demo I am too lazy not to post

Mybatis dynamic multi – data source implementation

Blog.csdn.net/u013360850/…

2.2. Global variable passing

In business development, the acquisition of data from current requesting users is a very common requirement. For example, in the scenario of placing an order, I obtain the user data through the request token resolution, and the user data may be used in the common MVC three layers. The more troublesome approach is to maintain a User parameter to pass in each method’s incoming parameter. The disadvantage of this is that all the methods associated with the current business scenario have one more User input.

User information may be different for each thread, but for a token, the maintenance of user information is single. You can parse the token at the Controller layer using interceptors/filters /AOP to get the User information, put it in a ThreadLocal variable, and make all methods related to the current thread share this variable, reducing the passing of method parameters.

2.3. Link tracing

In microservice architecture, link tracing is a common method when errors occur between calls between multiple services. The traceId is embedded in the thread variable of the request. According to the traceId, the log of the corresponding request in each business application can be found.

I’ve written an entry level article about spring-cloud-sleuth, if you’re interested in it

Juejin. Cn/post / 692300…

3.ThreadLocal principle resolution

To better understand ThreadLocal, it’s inevitable to read the source code. Wouldn’t it be nice to build a rocket for an interview? Focus on the set(), get(), remove() methods

3.1. The set () method

Public void set(T value) {// get the currentThread Thread T = thread.currentthread (); // Get the ThreadLocalMap data that maintains the current thread variable, a hashMap-like structure ThreadLocalMap map = getMap(t); // If the thread already has a Map, call map.set if (Map! = null) map.set(this, value); Set else createMap(t, value); // If no Map exists, create a new Map and then set else createMap(t, value). }Copy the code

A ThreadLocalMap data structure appears in the set method. Click there to see

static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}}Copy the code

An entry structure is maintained to maintain node data. Carefully, students should have found that the entry structure inherits WeakReference. As can be seen from the construction method, the Key of ThreadLocalMap is maintained by soft reference. This place is important, and why is important, more on that later.

Click on that and you’ll see that the ThreadLocal member variable defines this statement

ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code

The appearance of this sentence indicates that a single ThreadLocalMap is maintained independently for each thread, and that a thread can have multiple ThreadLocal variables.

3.2. The get () method

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(); }Copy the code
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; }Copy the code
protected T initialValue() {
    return null;
}
Copy the code

The get() method is generally simple, with the key logic code attached. When you call get(), if there is a value, it returns it. When there is no value, you call setInitialValue() to get the value, where the initialized value is null. If an assignment is removed, a direct call to get() will return null.

3.3. The remove () method

//ThreadLocal public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m ! = null) m.remove(this); }Copy the code
//ThreadLocalMap
private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}
Copy the code

The remove method is called to determine if ThreadLocalMap exists in the current thread. If so, threadLocalMap.remove (key) is called. Traversing the linked list structure removes an entry node

3.4. Summarize

1.Thread maintains a reference to ThreadLocalMap, in which the key of ThreadLocalMap is maintained by WeakReference

2.ThreadLocal itself does not store values. Instead, a ThreadLocal can assign, retrieve, and delete thread variables by manipulating ThreadLocalMap.

4. Memory leak of ThreadLocal

Take a look at the stack maintenance diagram for ThreadLocal in the JVM

The entry reference to value is a strong application, and the key reference is a weak one

A weak reference, application of concepts: blog.csdn.net/zalu9810/ar…

If an object is only referenced by a weak reference, the object will be reclaimed whenever GC occurs, regardless of whether there is enough memory space.

The problem is that if the QPS of a ThreadLocal variable is so high that it’s being requested, and you’re calling the set(),get() method instead of the remove method, then when GC occurs. The association between entry and ThreadLocal is broken, the Key is reclaimed, and the value is associated with a strong connection. In this case, the value is still reachable with garbage collection reachability analysis, but from a business perspective, the value will never be reachable, resulting in a memory leak.

Therefore, when using ThreadLocal, you must call the remove method, otherwise there is a problem, and it is difficult to troubleshoot.

5.InheritableThreadLocal

It is not possible for all work in daily life to be based on single threaded operations. In the case of multithreading, can a variable called ThreadLocal, defined in the main thread, be accessed by a child thread? Give it a try

public class Demo { public static final ThreadLocal t = new ThreadLocal(); public static void main(String[] args) { t.set("test"); new Thread(() -> { System.out.println("new:"+t.get()); }).start(); System.out.println("main:"+t.get()); }} Output: main:test new:nullCopy the code

Fuck, I can’t. It’s getting cold.

Don’t panic, the Java gods are already thinking about our everyday business scenarios, passing variables in parent and child threads using InheritableThreadLocal, and ThreadLocal is its parent class. Try the code above

public class Demo { public static final ThreadLocal t = new InheritableThreadLocal(); public static void main(String[] args) { t.set("test"); new Thread(() -> { System.out.println("new:"+t.get()); }).start(); System.out.println("main:"+t.get()); }} Output: main:test new:testCopy the code

Nice~

Let’s take a look at what InheritableThreadLocal really is.

Click to go to this class, which is short and overrides three ThreadLocal methods

Public class InheritableThreadLocal<T> extends ThreadLocal<T> { ParentValue (parentValue) {return parentValue; } // Map is replaced by inheritableThreadLocals, Rather than threadLocals ThreadLocalMap getMap (Thread t) {return t.i nheritableThreadLocals; } // Create map using inheritableThreadLocals, Rather than threadLocals void createMap firstValue (Thread t, t) t t.i nheritableThreadLocals = new ThreadLocalMap (this, firstValue); }}Copy the code

In the second and third methods, it is simpler to use inheritableThreadLocals to maintain variables. Let’s focus on the first method, which traces back to the init() method in the Thread class, where the initialization code has this sentence

if (inheritThreadLocals && parent.inheritableThreadLocals ! = null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);Copy the code

The parent thread’s thread Map is assigned to the child thread, and communication between the parent thread and the child thread is achieved.

nice~

6.TransmittableThreadLocal

After all, the use of direct new threads in daily multithreaded programming is still a minority, and a more standardized way is to use thread pools. So if you use InheritableThreadLocal in the main thread, can you pass in thread variables in the main thread? So let’s try that

public class Demo { public static final ThreadLocal t = new InheritableThreadLocal(); public static void main(String[] args) { t.set("test"); ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.submit(()->{ System.out.println(t.get()); }); System.out.println("main:"+t.get()); }} Output main:test testCopy the code

It worked, nice

Measure the concurrency

public class Demo { public static final InheritableThreadLocal t = new InheritableThreadLocal(); public static void main(String[] args) { t.set("test"); ExecutorService executorService = Executors.newFixedThreadPool(1); Runnable runnable1 = () -> {system.out.println ("new before :" + t.gt ())); }; Runnable runnable2 = ()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println ("new after modification :"+ t.gt ()); }; executorService.submit(runnable1); System.out.println("main before :"+ t.gt ()); t.set("test1"); System.out.println("main after modification :"+ t.gt ()); executorService.submit(runnable2); System. The out. Println (" main finally: "+ t.g et ()); }} Output: main Before modification :test new Before modification :test main After modification :test1 main At last :test1 new After modification :testCopy the code

When you change an InheritableThreadLocal variable, you’re left with the same value.

To review the difference between a thread pool and a new thread pool, the core thread pool of a thread pool is used repeatedly if the core thread pool is released for opening, but the maintenance variables in an InheritableThreadLocal are assigned only once when a new thread city is created. Cannot get the updated InheritableThreadLocal variable value.

So how can we solve this problem? TransmittableThreadLocal to help you resolve it. This is a three-party library developed by Ali, which is specially used to solve the problem of thread variable communication in the thread pool. The specific principle here is not expanded and analyzed, and it is introduced by the official website

Github.com/alibaba/tra…

So let’s use it

public class Demo { public static final ThreadLocal t = new TransmittableThreadLocal(); public static void main(String[] args) { t.set("test"); ExecutorService executorService = Executors.newFixedThreadPool(1); Runnable runnable1 = () -> {system.out.println ("new before :" + t.gt ())); }; Runnable runnable2 = ()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println ("new after modification :"+ t.gt ()); }; executorService.submit(TtlRunnable.get(runnable1)); System.out.println("main before :"+ t.gt ()); t.set("test1"); System.out.println("main after modification :"+ t.gt ()); executorService.submit(TtlRunnable.get(runnable2)); System. The out. Println (" main finally: "+ t.g et ()); }} Output: main Before modification :test new Before modification :test main After modification :test1 main At last :test1 new After modification :test1Copy the code

nice~

7. To summarize

This article focuses on the application of ThreadLocal in various daily business development scenarios, and extends the principles and methods of InheritableThreadLocal communication between parent and child threads. Finally, we introduce Ali’s TransmittableThreadLocal to support the thread variable communication between the main thread and the thread pool. We hope that we can have a system understanding and help of ThreadLocal.

Reference 8.

Juejin. Cn/post / 684490…

Juejin. Cn/post / 684490…

9. Contact me

Nailing: louyanfeng25

WeChat: baiyan_lou