“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

The introduction

There are plenty of articles about ThreadLocal on the web, and many of them are already well written. However, many students responded that some parts were not explained very clearly, or there is a certain doubt did not think very clearly. Therefore, this article will take a thorough and in-depth look at ThreadLocal with some common questions, ThreadLocal source code, application examples and considerations. Hopefully, by the end of this article you’ll have a good grasp of ThreadLocal.

What is a ThreadLocal? What can it do? Before we get to ThreadLocal, let’s take a look at how its designers describe ThreadLocal.

ThreadLocal provides a data access mechanism corresponding to a separate thread, implementing the ability to isolate variables between threads and obtain or set them independently during the thread life cycle. ThreadLocal can come in handy if we want to pass parameters in a thread but don’t want to use them as method parameters. It’s worth noting, however, that ThreadLocal doesn’t solve the variable sharing problem. In fact, the name of a ThreadLocal variable gives a general idea of what it does, so it’s important to name variables as they are. If you still don’t understand, it doesn’t matter. We can deepen our understanding through the following scenario.

Suppose there is only one database connection. Clients 1, 2, and 3 all need to acquire database connections to perform specific database operations, but only one thread can acquire connections at a time, and the other threads have to wait. Therefore, the database access efficiency is not high.

Is there anything we can do to prevent threads from waiting? The root cause of the above problem is that database connections are shared variables and only one thread can operate at a time. If all three threads have their own connections to the database and are isolated from each other, there will be no waiting problems. We can then use ThreadLocal to isolate variables in different threads. As you can see, ThreadLocal is a way of trading space for time.

ThreadLocal is the secret to thread isolation

From the previous article, we learned that ThreadLocal can implement thread-level isolation of variable access. So how does it work? This requires a combination of Thread and ThreadLocal source code to solve the mystery of Thread isolation implemented by ThreadLocal.

public class Thread implements Runnable {.../* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null; . }Copy the code

Thread has a function called threadLocals, which is of type ThreadLocalMap, an internal class of ThreadLocal. Let’s take a look at the definition of ThreadLocalMap. A ThreadLocalMap is an array of entries. The key corresponding to the Entry is an instance of ThreadLocal, and the value is the actual variable value.

public class ThreadLocal<T> {...static class ThreadLocalMap {
     
      static class Entry extends WeakReference<ThreadLocal<? >>{
            /** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}...// The underlying data structures are arrays
       privateEntry[] table; . }... }Copy the code

By looking at the above source code, if it is not easy to understand, let’s combine the real example to understand. We all have Alipay accounts, through which we manage our bank cards, balances, and other financial services.

Let’s make an analogy with Alipay and Alipay account. Assume that ThreadLocal is Alipay, each Alipay account is actually a separate Thread, and the balance attribute in the account is equivalent to ThreadLocalMap, the private attribute of Thread. In our daily life, we do not recharge or consume the account balance directly through the account, but with the help of Alipay for maintenance. This means that each thread does not operate directly on a ThreadLocalMap, but with a ThreadLocal.

How does ThreadLocal manage the private properties of threads? We need to look at the source code for Thread to perform set and GET operations. As you can see from the following ThreadLocal source code, you need to retrieve the current thread that is performing the operation, and then perform the operation based on the thread or its private ThreadLocalMap attribute.

In the process of data acquisition, the same process is followed: the current thread is obtained first, and then the corresponding ThreadLocalMap attribute in the thread is obtained for the subsequent value acquisition.

After analyzing the above source code, we can conclude that ThreadLocal allows thread-isolated access to variables by using the ThreadLocalMap property of Thread. Because they operate on properties of the thread itself, variable values in other threads are not affected, enabling thread-level data modification isolation.

Why are OOM problems with ThreadLocal?

Memory leak Demo

ThreadLocal is known to leak memory if used incorrectly, so let’s examine the causes of memory leaks.

/ * * *@author mufeng
 * @descriptionTest ThreadLocal memory overflow *@date 2022/1/16 19:01
 * @since* /
public class ThreadLocalOOM {

    /** * test thread pool */
    private static Executor threadPool = new ThreadPoolExecutor(3.3.40,
            TimeUnit.SECONDS, new LinkedBlockingDeque<>());


    static class Info {
        private byte[] info = new byte[10 * 1024 * 1024];
    }

    private  static ThreadLocal<Info> infoThreadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            threadPool.execute(() -> {
                infoThreadLocal.set(new Info());
                System.out.println("Thread started:" + Thread.currentThread().getName());
            });
            Thread.sleep(100); }}}Copy the code

Manually after GC, we can find the heap heap memory footprint still has more than 30 m, as code, active threads in the thread pool will have three, the corresponding value of 10 m, shows that under the condition of the thread is still alive, the corresponding value has not been recycled, so the existence of memory leak, if there is a large number of threads, OOM will appear.

When we modify the code to do remove in the thread, after manual GC we find that the heap is close to zero, and objects that were not previously collected have been collected.

Analysis of memory leaks

This is an example of a ThreadLocal memory leak, so let’s take a closer look at what’s behind it. The key of the Map is a virtual reference that can be reclaimed during GC. However, the problem is that the value of the key is a strong reference. As long as the thread is alive, the chain of references will exist consistently. There is a risk of OOM if there is a large number of threads. Remember to explicitly call the remove method when using ThreadLocal to clean up memory leaks.

Parameter passing for parent and child threads

At this point, I believe you have a good understanding of how ThreadLocal works. When using ThreadLocal, we perform set and GET operations in the same thread. Can we obtain set and GET operations normally in parent threads? With that in mind, let’s take a look at the following code.

/ * * *@author mufeng
 * @descriptionParent thread argument passing *@date2022/1/16 hastily *@since* /
public class InheritableThreadLocalMain {

    private static final ThreadLocal<String> count = new ThreadLocal<>();

    public static void main(String[] args) {

        count.set(Parent-child thread parameter pass!!);
        System.out.println(Thread.currentThread().getName() + ":" + count.get());

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":"+ count.get()); }).start(); }}Copy the code

Unlike the previous code, ThreadLocal is set in the main thread, but the fetch is actually done in the child thread below the main thread.

See this running result, I don’t know if you are right. In fact, if you understand the core of the above, the problem should be easy to analyze. When a ThreadLocal thread fetched data, it would need to fetch data from the current thread. If the parent thread fetched data from a ThreadLocal thread, it would not be able to fetch data from the parent thread. But in the real project development, we will often encounter the need to pass the variable value of the parent thread to the child thread for processing, so how to achieve it? That’s where InheritableThreadLocal comes in.

/ * * *@author mufeng
 * @descriptionParent thread argument passing *@date2022/1/16 hastily *@since* /
public class InheritableThreadLocalMain {

    private static final ThreadLocal<String> count = new InheritableThreadLocal<>();

    public static void main(String[] args) {

        count.set(Parent-child thread parameter pass!!);
        System.out.println(Thread.currentThread().getName() + ":" + count.get());

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":"+ count.get()); }).start(); }}Copy the code

How does InheritableThreadLocal implement parent-child thread parameter passing? I still want to see the implementation principle in the source code. In fact, in the Thread source code, we have an InheritableThreadLocal private property as well as an InheritableThreadLocal private property.

public class Thread implements Runnable {
    
     /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal class. */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; .public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null.true);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {.../ / key
         if(inheritThreadLocals && parent.inheritableThreadLocals ! =null)
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); . }... }Copy the code

During the process of thread initialization, whether the inheritableThreadLocals attribute in the parent thread is null is determined. If not, the value needs to be copied, thus realizing the value transfer between the parent thread and the child thread.

conclusion

This paper mainly carries out a relatively comprehensive analysis of ThreadLocal, from its use scenario, principle and source code analysis, OOM cause and some attention on the use of ThreadLocal, I believe that through the study of this paper, you will have a deeper understanding of ThreadLocal.