ThreadLocal actual combat and detailed explanation

This time I will introduce to you the important tool ThreadLocal. The explanation is as follows. It also introduces the scenarios in which memory leaks occur, how to reproduce memory leaks, and how to use it correctly to avoid memory leaks.

  1. What is ThreadLocal? What are its uses?
  2. How to use ThreadLocal
  3. ThreadLocal principle
  4. What are the pitfalls and precautions when using ThreadLocal?

1. What is ThreadLocal? What are its uses?


    First introduce the attribute threadLocals in the Thread class:

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

    We found that Thread does not provide a method for setting and accessing the member variable threadLocals. So how do we operate the instance threadLocals parameters of each thread? At this time our protagonist: ThreadLocal appears.
   So there is a summary: ThreadLocal is the manager of the threadLocals attribute in thread Thread . In other words, the results of our ThreadLocal get, set, and remove operations are all threadLocals storage, access, and deletion operations for the current thread Thread instance. Similar to a developer's tasks, the product manager cannot control them. The product manager can only assign tasks to developers through the technical leader. Here is another example to further illustrate the relationship between them: that is to say, the results of our ThreadLocal get, set, and remove operations are all threadLocals storage, access, and deletion operations for the current thread Thread instance. Similar to a developer's tasks, the product manager cannot control them. The product manager can only assign tasks to developers through the technical leader. Here is another example to further illustrate the relationship between them:

Insert image description here

  1.  Everyone has a bank card
  2. Everyone has a certain balance on each card.
  3. Everyone must go through the bank's management system to obtain their bank card balance.
  4. Everyone can only obtain the balance information held by their own card, and others cannot access it.

Insert image description here

Map to the ThreadLocal we want to say

  1. card is similar to Thread
  2. Card balance attributes, card number attributes, etc. are similar to Treadlocal’s internal attribute collection threadLocals
  3. cardManager is similar to the ThreadLocal management class 

So what are the application scenarios of ThreadLocal?

In fact, we have inadvertently used the convenience provided by ThreadLocal all the time. If switching between multiple data sources is unfamiliar to you, then the declarative transactions provided by spring are all too familiar. We use it all the time during the research and development process, and spring The important implementation basis of declarative transactions is ThreadLocal, but no one has delved into the implementation mechanism of spring declarative transactions. When I have the opportunity later, I will introduce to you the principles and implementation mechanism of spring declarative transactions.
    It turns out that ThreadLocal is so powerful, but it is rarely used by application developers. At the same time, some developers are afraid of trying ThreadLocal due to memory leaks and other potential problems. I am afraid this is the biggest misunderstanding about ThreadLocal. We will analyze it carefully later. As long as it is used correctly, method, there is no problem. If there is a problem with ThreadLocal, isn't spring's declarative transaction the biggest potential danger to our program?

2.How to use ThreadLocal

 For a more intuitive experience, ThreadLocalwe assume the following scenario

  1. We generate an ID for each thread.
  2. Once set, it cannot be changed during the thread life cycle.
  3. Duplicate IDs cannot be generated during container activity 

We create a ThreadLocal management class:

Write picture description here

 The test program is as follows: we use the same thread to get continuously, test whether the id changes, and release it after the test is completed.

Write picture description here

In the main program, we start multiple threads to test whether there will be any influence between blocked threads.

 Write picture description here

Not surprisingly our results are:

Write picture description here

Result: It is true that the IDs are different between different threads, but the IDs of the same thread are the same.

 3.ThreadLocal principle

①ThreadLocal class structure and method analysis:

 Insert image description here

As can be seen from the above figure: ThreadLocalthree methods get, set, remove and internal class `ThreadLocalMap

②The relationship between ThreadLocal and Thread:

Insert image description here

From this picture, we can intuitively see the attribute threadLocals in Thread. As a special Map, its key value is our ThreadLocalinstance, and the value value is the value we set.

③ThreadLocal operation process:

Let's take the get method as an example:

Insert image description here

Among them, getMap(t) returns the threadlocals of the current thread, as shown below, and then uses the current ThreadLocal instance object as the key to obtain the value in the ThreadLocalMap. If it is the first time to come in, call setInitialValue()

 Insert image description here

Insert image description here 

The set process is also similar: 

 Insert image description here

Note: ThreadLocalThis can t.threadLocalsbe done directly because Threadunder ThreadLocalthe same package, Thread can also be directly accessed ThreadLocal.ThreadLocalMap threadLocals = null;to declare properties. 

 4. What are the pitfalls and precautions when using ThreadLocal?

I often see horrifying headlines on the Internet that ThreadLocal causes memory leaks. This usually makes some developers who do not have a thorough understanding of ThreadLocal at the beginning dare not use it rashly. The less used it is, the stranger it becomes. This makes us miss out on better implementation solutions, so only by daring to introduce new technologies and stepping into pitfalls can we continue to make progress.
Let’s take a look at why ThreadLocal can cause memory leaks. In what scenarios can it cause memory leaks?
    Let’s first review what a memory leak is, and what the corresponding memory overflow is:
① Memory overflow: Memory overflow, there is not enough memory for the applicant to use.
②Memory leak: Memory leak. After the program applies for memory, it cannot release the applied memory space. The accumulation of memory leaks will eventually lead to memory overflow.
Apparently the memory was not released due to irregular use of TreadLocal.

Insert image description here 

In the red box we see a special class WeakReference. This class is also rarely used by application developers. Let’s briefly introduce it here.


    Since WeakReference is about to be recycled in the next gc, why does our program not have any problems?


    ①So we test the recycling mechanism of weak references:

Insert image description here 

This kind of strong reference will not be recycled.

 Insert image description here

No strong references here will be recycled.

The above demonstrates the recycling of weak references. Let's take a look at the recycling of weak references in ThreadLocal.

    ②Weak ThreadLocalreference recycling situation

 Insert image description here

As shown in the figure above, we have no external strong reference to the ThreadLocal object as the key. The next gc will definitely generate data with a key value of null. If the thread does not end in time, a strong reference chain Threadref–>Thread–>ThreadLocalMap– will inevitably occur
. >Entry, so this will cause a memory leak.

Below we simulate and reproduce the memory leak caused by ThreadLocal:
1. For better effect, we set the storage value of our treadlocals to a list of 10,000 strings:
 

class ThreadLocalMemory {
    // Thread local variable containing each thread's ID
    public ThreadLocal<List<Object>> threadId = new ThreadLocal<List<Object>>() {
        @Override
        protected List<Object> initialValue() {
            List<Object> list = new ArrayList<Object>();
            for (int i = 0; i < 10000; i++) {
                list.add(String.valueOf(i));
            }
            return list;
        }
    };
    // Returns the current thread's unique ID, assigning it if necessary
    public List<Object> get() {
        return threadId.get();
    }
    // remove currentid
    public void remove() {
        threadId.remove();
    }
}

The test code is as follows:

public static void main(String[] args)
            throws InterruptedException {

        //  为了复现key被回收的场景,我们使用临时变量
        ThreadLocalMemory memeory = new ThreadLocalMemory();

        // 调用
        incrementSameThreadId(memeory);

        System.out.println("GC前:key:" + memeory.threadId);
        System.out.println("GC前:value-size:" + refelectThreadLocals(Thread.currentThread()));

        // 设置为null,调用gc并不一定触发垃圾回收,但是可以通过java提供的一些工具进行手工触发gc回收。
        memeory.threadId = null;
        System.gc();

        System.out.println("GC后:key:" + memeory.threadId);
        System.out.println("GC后:value-size:" + refelectThreadLocals(Thread.currentThread()));

        // 模拟线程一直运行
        while (true) {
        }
    }

How do we know that there is a memory leak in the memory at this time?
We can use some commands provided by jdk to dump the current heap memory. The commands are as follows:
jmap -dump:live,format=b,file=heap.bin <pid>
Then we use the MAT visual analysis tool to view the memory and analyze the survival status of the object instance: 

Insert image description here 

Insert image description here 

First open our tool to prompt our memory leak analysis:

 Insert image description here

What we can determine here is that the Entry.value of the ThreadLocalMap instance has not been recycled.
    Finally, we need to determine whether Entry.key is still there? Open the Dominator Tree, search for our ThreadLocalMemory, and find that there is no surviving instance.Insert image description here 

 Insert image description here

Above we have reproduced the memory leak caused by improper use of ThreadLocal. The demo is here.
    So we summarized the prerequisites for memory leaks when using ThreadLocal:
① The ThreadLocal reference is set to null, and there are no set, get, or remove operations later.
②The thread keeps running without stopping. (Thread pool)
③Garbage collection is triggered. (Minor GC or Full GC)
    We see that the conditions for memory leaks in ThreadLocal are still very harsh, so we only need to destroy one of the conditions to avoid memory leaks, but in order to better avoid this situation from happening, we follow the rules when using ThreadLocal The following two small principles:
    ①ThreadLocal is declared as private static final.
         Private and final try to prevent others from modifying the change reference.
         Static is expressed as a class attribute and will only be recycled when the program ends.
    ②Be sure to call the remove method after using ThreadLocal.
        The simplest and most effective way is to remove it after use.

 

Guess you like

Origin blog.csdn.net/bingxuesiyang/article/details/123412581