ThreadLocal Technology Sharing

To learn a thing, we must first know why we introduce it, and what we can do with it. So let's take a look at what ThreadLocal does for us first, and then take a look at its implementation principle.

If ThreadLocal looks like "local thread" from its name, it can only be said that the name is not very good, and it is easy to misunderstand. ThreadLocalVariable (thread local variable) should be a better name. Let's take a look at the official description of ThreadLocal:

This class provides thread-local variables. These variables are different from their normal counterparts because each thread that accesses a variable (via its get or set method) has its own local variable, which is independent of the initial copy of the variable. ThreadLocal instances are usually private static fields in the class, and they want to associate the state with a certain thread (for example, user ID or transaction ID).

1. Each thread has its own local variables

Each thread has a context independent of other threads to save this variable, and the local variables of a thread are invisible to other threads (there are conditions, explained later)

2. Initialized copy independent of variables

ThreadLocal can give an initial value, and each thread will get a copy of this initial value, so as to ensure that different threads have a copy.

3. The state is associated with a certain thread

ThreadLocal is not used to solve the problem of shared variables. It does not exist for coordinating thread synchronization, but a mechanism introduced to facilitate each thread to handle its own state. Understanding this is essential to the correct use of ThreadLocal

When to use:

Give a few examples to illustrate:

1. For example, there are many ways to process a very complex business in a thread. Then, using ThreadLocal can replace the explicit transmission of some parameters;

2. For example, to store user sessions. The characteristics of Session are very suitable for ThreadLocal, because the Session is valid in the current session period before, and the session is destroyed when it ends. Let us first analyze the process of a web request in general but incorrectly:

  • The user visits the web page in the browser;
  • The browser initiates a request to the server;
  • The service handler on the server (such as tomcat) receives the request and starts a thread to process the request, during which the Session will be used;
  • Finally, the server returns the request result to the client browser.

From this simple access process, we can see that this Session is generated and used in the process of processing a user session. If you simply understand that a session of a user corresponds to an independent processing thread on the server side, then use ThreadLocal to store the Session. It couldn't be more suitable. However, server software such as tomcat uses thread pool technology, and it is not strictly true that a session corresponds to a thread. It is not that this situation is not suitable for ThreadLocal, but that the previous Session must be cleaned up every time a request comes in. Generally, interceptors and filters can be used to achieve.

3. In some multi-threaded situations, if thread synchronization is used, performance will be affected when concurrency is relatively high, and it can be changed to ThreadLocal. For example, the high-performance serialization framework Kyro will use ThreadLocal to ensure high performance and Thread-safe

4. There are also ThreadLocal that can be used in thread managers, database connections, etc.;

Now we first look at a piece of code:

image

operation result:

image

This example tells us that the variables between each thread do not affect each other.

Then let's look at an example:

image

Output result:

image

Huh, why is each value different? Didn’t it say that good things do not affect each other?

At this time, I have to take out the paintbrush I haven't moved for so long, to explain to you why this happens.

Let's take a look at the difference between Demo1 and Demo2.

Demo1:

image

Demo2:

image

Here comes the problem. Demo1 returns a basic type of 0 every time. But indexnum is an object. So every time it points to the same object, in order to deepen our understanding, we draw a picture to show it.

image

So ThreadLocal only saves a copy of the address of the object, and the objects we initialize all point to the same address, so there will be such a problem. So how do we solve this problem. it's actually really easy

image

Just need a new object every time new, so that it will not point to the same address.

Let's look at the output result:

image

Next, let's take a look at the internal source code, Xiao Dai and take you to read the ThreadLocal source code

image

There are several methods in ThreadLocal. The main one is

public T get() { }

public  void set(T value) { }

public  void remove() { }

protected T initialValue() { }

The get() method is used to obtain a copy of the variable saved by ThreadLocal in the current thread, set() is used to set a copy of the variable in the current thread, and remove() is used to remove the copy of the variable in the current thread. InitialValue() is a The protected method is generally used to rewrite during use, it is a lazy loading method

1: get method:

image

  1. Get the current thread first
  2. Determine whether the current thread contains ThreadLocalMap
  3. If the map is null or the local variable in the map is empty, create the initial value

image

When creating the initial value, it will determine whether the map exists,

  1. If it does not exist, initialize the map and set the key and value of the map
  2. If it exists, directly set the key and value of the map

image

This is the get operation of ThreadLocal.

2: set method:

image

It is also very similar to get the current thread first, and then from the current thread's ThreadLocalMap if the set value exists, then the set does not exist, then initialize and set the value

3: Remove method:

image

Get the current thread, the current thread instance is the key of the map to get and remove.

This is the three commonly used methods of ThreadLocal.

Next, let's learn about ThreadLocalMap in ThreadLocal

image

Through the previous analysis, we know that when using ThreadLocal to save a value, an Entry object will be inserted into the array in ThreadLocalMap. It stands to reason that key-value should be stored in the Entry object with a strong reference, but in the implementation of ThreadLocalMap, the key It is saved in the WeakReference object.

This leads to a problem. ThreadLocal will be recycled when GC occurs when there is no external strong reference. If the thread that created the ThreadLocal continues to run, then the value in the Entry object may not be recycled, and memory leaks may occur. .

image

Then some people will ask why weak references are used. In fact, this has something to do with the design philosophy of Java. Java has always advocated weakening pointer management. So the key is how to quote. Let's compare the two situations

  1. The key uses a strong reference: the referenced ThreadLocal object is recycled, but the ThreadLocalMap still holds a strong reference to the ThreadLocal. If it is not manually deleted, the ThreadLocal will not be recycled, resulting in an Entry memory leak.
  2. Key uses weak reference: the referenced ThreadLocal object is recycled. Because ThreadLocalMap holds a weak reference to ThreadLocal, even if it is not manually deleted, ThreadLocal will be recycled. The value will be cleared the next time ThreadLocalMap calls set, get, and remove.

Comparing the two cases, we can find that: Since the life cycle of ThreadLocalMap is as long as Thread, if the corresponding key is not manually deleted, it will cause memory leaks, but the use of weak references can provide an additional layer of protection: weak references to ThreadLocal will not leak memory. The corresponding value will be cleared the next time ThreadLocalMap calls set, get, and remove.

Therefore, the source of the ThreadLocal memory leak is: Since the life cycle of ThreadLocalMap is as long as Thread, if the corresponding key is not manually deleted, it will cause a memory leak, not because of weak references.

In summary

When we use ThredLocal again, we must call the remove method after we use it. clear data.

Lessons of blood and tears! ! !

p3 accident

I still remember that day on March 26, I will not forget. After waking up at noon that day, my friend took me to luckin coffee to buy a bottle of coffee, and I felt happy. It's another log with coffee and typing code. At this time, the internal technical group suddenly reported that the merchant login information was serialized. Through, my brain is buzzing~ At this time, I was thinking why we did not change the code why such a problem occurred.

At this time, I realized the seriousness of the problem and rushed back to the office. At this time, the breakdown time had exceeded 5 minutes. I opened the ci interface and rolled back immediately. Fortunately, the merchant was released quickly, and the rollback was completed within 5 minutes. The operation group was notified to let the merchant refresh the page and the failure was restored.

In fact, when I rolled back, I realized what the problem was (it was not cleared after ThreadLocal was used), and what immediately came to my mind was why there was no problem before and now it caused this problem. Later, the upgrade to the spring package version resulted in the execution order of aop:

image

We have made a bottom-up solution in our code, which is to delete the currently used ThreadLocal information through aop. But after the spring package version was upgraded, aop first implemented this solution, and then executed other scenarios using ThreadLocal.

After the result was cleared, the Get operation was performed again, which caused other users to access and obtained user information in other thread pools (tomcat thread pool reuse)

In response to this situation, a correction was made to the bottom line. Use filter to achieve the bottom line solution because the execution order is

before:

Aop does not specify order, so there is no way to deal with it.

after:

Set up a filter outside, set and remove ThreadLocal inside

image

image

Configure the filter at the outermost layer. The ThreadLocal setting is only placed in the Filter. After the method is executed, the ThreadLocal is cleared in the filter.

ps: Regardless of whether there is this layer of pockets or not, after using ThreadLocal, it is revome dropped in the final code block.

 

Guess you like

Origin blog.csdn.net/suifeng629/article/details/107119773