Still don’t understand ThreadLocal? That's because you haven't read this article yet

Compared with Synchronized, what is its function

Both ThreadLocal and Synchronized are used to solve multi-threaded concurrent access. But ThreadLocal and Synchronized are fundamentally different. Synchronized is the use of a lock mechanism so that variables or code blocks can only be accessed by one thread at a certain time.

From the name, we can see that ThreadLocal is called a thread variable, which means that the variable filled in ThreadLocal belongs to the current thread, and the variable is isolated from other threads. ThreadLocal creates a copy of the variable in each thread, so each thread can access its own internal copy variable.

It is very easy to understand from the literal meaning, but not so easy from the point of view of actual use. As a frequently asked question in an interview, the usage scenarios are also quite rich.

  1. When transferring objects across levels, using ThreadLocal can avoid multiple transfers and break the constraints between levels.
  2. Hierarchical isolation between threads.
  3. Perform transaction operations to store thread transaction information.
  4. Database connection, Session session management.

You should now have a general understanding of ThreadLocal. Let's take a look at how to use it.

How to use ThreadLocal

Since the function of ThreadLocal is to create a copy for each thread, let's use an example to verify:

 

 

From the results, we can see that each thread has its own local value. In other words, the value of threadLocal is thread and thread separation. The specific principle can draw the following how ThreadLocalMap stores data in different threads.

If it is the first time to learn ThreadLocal, friends may be confused. I didn’t understand ThreadLocal. Tell me about ThreadLocalMap? Don't worry, we will continue to look down.

 

The usage scenario of ThreadLocal-database connection

We know that the most criticized database connection pool is the creation and closing of connections. This requires a lot of resources and time. Our ThreadLocal can also help us solve this problem.

 

This is a database connection management class. When we use a database, the first step is to establish a database connection. Then close it when it's used up. This has a very serious problem. If a user frequently uses the database, then multiple connections and shutdowns need to be established. In this way, our server may be overwhelmed, so what should we do? If there are 10,000 clients, the server pressure is even greater.

At this time, it is best to use ThreadLocal. Because ThreadLocal creates a copy in each thread. And it can be used anywhere inside the thread. The threads do not affect each other. In this way, there is no thread safety problem, it will not seriously affect the performance of the program, and avoid the frequent creation and destruction of the connection. ( Of course, in practice, we have a database connection pool to handle, but our purpose is very clear, to avoid frequent creation and destruction of connection objects! )

The above mainly explained a basic case, and then analyzed why ThreadLocal is used when connecting to the database. Below we analyze the working principle of ThreadLocal from the perspective of source code.

ThreadLocal source code analysis

Introduction to ThreadLocal class interface

The ThreadLocal class interface is very simple, there are only 4 methods, let's first understand:

  1. void set(Object value); //Set the thread local variable value of the current thread
  2. public Object get(); //This method returns the thread local variable corresponding to the current thread
  3. public void remove(); //When the value of the thread local variable is deleted, the purpose is to reduce the memory usage. This method is a new method of JDK5.0. It should be pointed out that after the current thread ends, the local variables of the corresponding thread will be automatically garbage collected, so calling this method to clear the thread’s local variables is not a necessary operation, but it Can speed up the speed of memory recovery.
  4. protected Object initialValue() //Returns the initial value of the thread's local variables. This method is a protected method, which is obviously designed to allow subclasses to cover. This method is a delayed call method, executed when the thread calls get() or set(Object) for the first time, and only once. If you do not write initialValue, then the first call to get() will return a null.
  5. public final static ThreadLocal<String> resourse = new ThreadLocal<String>();  resourse only represents a ThreadLocal object that can store String type. At this time, no matter what thread concurrently accesses this variable, write and read operations on it are all thread-safe.

 

Draw a classic flow chart step by step from the source code

According to the use process of ThreadLocal in actual development, we draw the classic flow chart that is spread everywhere on the Internet step by step. (Look carefully, 100% understand the interviewer sling!)

 

In fact, to draw this picture, only three lines of code are required. Note: ThreadLocal is set as a local method only for writing examples. If ThreadLocal is set as a local variable, it will lose its own feature of thread isolation. It's exactly the operation of a nuclear bomb hitting ants!

 

 

First, as in step 1, we create a new ThreadLocal object.

 

We know that if ThreadLocal is not set, there is no data, so we proceed to step 2 to start setting a value. Click into set to see the source code!

 

Clicking into the set method of ThreadLocal, we find that it gets the object of the current thread in the first step. Note that the life cycle of the current thread's object is synchronized with the current thread. So update the flowchart:

 

Then we get the ThreadLocalMap based on the current thread object (this ThreadLocalMap does not always exist, but to check whether we currently have this ThreadLocalMap object, if it does not exist, we will first create the object, otherwise we will directly obtain the ThreadLocalMap object). So the update process:

 

After obtaining the map object, we start to set the ThreadLocalMap object of the current thread.

 

Note that the key of the set here is this. The this object at this time is our ThreadLocal object, as shown in the figure:

 

So what does the set method of this ThreadLocalMap object do? We continue to go in and see.

 

We can see that. We reprocessed the data and put it into an Entry array. So what is this Entry array? First update the process:

 

Let's take a look at the structure of the Entry class.

 

We can see that the structure of Entry is very similar to a map, and the most important thing is here. The key of this Entry is a weak reference here. what? Weak references? What is this for? Don't worry, keep your doubts. Let's update our flow chart by following the above steps:

 

 

Finally, at this point, it coincides with our most classic picture. At this time we took a breath, and we are finally done! Do not! I can't stop talking. There is the most critical step.

We know that the characteristic of weak references is that after a GC, the connection with the object is broken. Then the program runs for a period of time, after a random GC, the entire memory graph looks like this. This is the final distribution of the data in the memory!

 

Then someone said again? Good guy, your picture is like this, can I still get the value through the ref.get() method! Shaoan, don't worry, this will take you to continue watching.

 

We found that when we get the ThreadLocal data of the current thread, we also get the current thread and delegate it to our ThreadLocalMap to query again. Then the process is like this.

 

From the existence purpose of step 1, we enter step 2 of the current thread to obtain the value data of the current thread whose key is ref. Do you feel like Mauser suddenly started! Can we finally call it a day? When you are ready to take a breath, I say not yet! Because the blogger had a doubt from the beginning. That is, when the reference of the ref object is disconnected by the key of my Entry, won't the key in my Entry become null ? We continue to reveal the answer.

 

Weak citation interpretation

We know that there are four types of references, strong, weak, and weak in Java, and the definition of weak reference is that as long as gc occurs, the reference chain will be broken. Let's test the weak reference with the program.

First, let's define a test class at will.

 

Second, we use weak references to refer to this class. We test whether the result of wrTest is null after a GC occurs in the following program.

 

 

At this point, we see that the object is indeed null. At this time, we change the wording.

 

 

Eh? Here comes the problem. Why is the value of this weak reference still available after a GC? Is the reference chain of weak references not disappearing? No, the truth is that our new Test() object at this time also happens to be pointed to by a strong test reference, so it cannot be recycled even if a GC occurs. This is completely consistent with the scenario where the entry key is disconnected from the reference chain of new ThreadLocal() in our ThreadLocal, but it is still not null.

 

 

We concluded that even if the object pointed to by the weak reference is disconnected from the weak reference, if the object is referenced elsewhere and cannot be recycled, then my weak reference can still get the value through the connection address before the disconnection. ( That is to say, the disconnection of the reference will not affect the addressing function of our reference. The disconnection of the reference will only cause the reference chain to be broken and the object will be recycled by the GC, but! If there is a strong reference at this time, then the weak reference We can continue to access in the absence of a reference chain of the object. ( here extended a bit. If the address of the object to force change, weak references will not be able to continue tracking ) ).

Take a simple case: suppose you buy a ticket to get on the train, find a seat and go in. But you with a bad memory, you can't find your seat when you go to the toilet and come back. At this time, the conductor can always find your seat number according to your ticket purchase file.

So far, the source code diagram of ThreadLocal can come to an end.

Why should the key in ThreadLocalMap be set as a weak reference?

The scene of ThreadLocal being recycled

 

First of all, emphasize that the premise of this assumption is that the usage of ThreadLocal is not in place, which is not elegant . Why do bloggers say that? Because ThreadLocal can have the ability to create a copy directly and independently in each thread, we usually decorate it with public static final. That is to say, this reference will never disappear without accident.

Some people will argue that although your reference is decorated with public static final and will not disappear, the thread will end? If you read the above process carefully, readers should have been very clear that our ThreadLocal value is obtained based on the ThreadLocalMap of the current thread. If the current thread ends, the ThreadLocalMap object of the thread will disappear together. The corresponding Entry will also disappear together (there will be an explanation later)

Causes of memory leaks

 

When we explained the process before, we mentioned that Entry in ThreadMap is a weak reference.

 

So at this time, we think backwards that the entry key in ThreadLocalMap is a strong reference, then when our ref is popped out of the stack, after line 1 is disconnected, Entry will always have a reference No. 2 pointing to the new ThreadLocal() object, resulting in this Objects can never be accessed and cannot be recycled, resulting in memory leaks.

 

In order to avoid this embarrassment, the entry key and the new ThreadLocal object are set as weak references. (Our two brothers can get in touch once, and it is okay to find you to collect debts in the future. I can't control whether you are dead or alive). Really regard the object as a tool person!

After setting as a weak reference, the memory model of a GC is as follows:

 

At this time, when ref pops from the stack, new ThreadLoal is isolated and helpless, only to be recycled. At this point, the most common memory leaks are explained.

Many online blogs are analyzed in this way. Although it can make sense in terms of light theory, but in fact, it does not have a deep understanding of ThreadLocal.

 

When step 1 is disconnected, step 2 is disconnected again through garbage collection, and the object is recovered in isolation. Here I confidently say: 2 actually broke up with the subject completely before 1 disconnected, no longer had anything to do with it! If you still don't understand, just continue to look at the above analysis process.

Entry key memory leak

The blog we read before said the most about the memory leak of the ThreadLocal object. However, in fact, we found that Entry actually has a leak. As shown in the figure, due to our successful recycling of ThreadLocal objects, our keys "finally" become null. But our value still exists, so the value of this group of data cannot be accessed because the key is null, which leads to a memory leak.

Yeah! This can be done, no one has ever mentioned it in the blog I read before! Don't worry, let's see how ThreadLocal responds.

 

Set optimization

 

At this time, when the key value corresponding to Entry's subscript i is null, it means that the key has been recycled, so just continue to occupy the position, anyway, the key is null and it is useless.

Get optimization

 

 

 

 

As you can see, the way get finds that the key is null is to forcibly delete it directly from the Entry.

Remove method

 

Remove is the way we actively trigger to clean up the Entry. The same method is called at the bottom of the get method. Can accelerate our leaked memory recovery. Therefore, if the reference in the stack becomes null, we can call the remove() method again to clean up the Entry in the ThreadLocalMap. (More time-sensitive)

 

Optimize when thread exits

Finally, when the thread exits, the Thread class will clean up. This includes cleaning up ThreadLocalMap.

The thread exits the exit() method of execution.

 

 

 

ThradLocal can be set as a local variable, it is possible but meaningless, and there is a risk of memory leaks

So much about memory leaks! In fact, we found that the cause of the memory leak is that this ThreadLocal is set to a local variable, which causes the ThreadLocal object to be recycled before the thread ends. At this time, there will be a risk that memory leaks can not be released until the thread ends. If you must write this way, then you must remember to call the remove() method when the ThreadLocal object is recycled to release the memory in time.

In addition, if threadLocal is set to a local variable, other methods in the same thread cannot obtain the object. This also deviates from the original intention of ThreadLocal to share the same variable in the same thread. The nuclear bomb kills the ants.

Incorrect use of ThreadLocal makes thread unsafe

 

 

It can be seen from the figure that when ThreadLocal operates on the same object, all operations point to the same instance. If you want the above program to run normally, you need to hold a new instance for each ThreadLocal.

Guess you like

Origin blog.csdn.net/weixin_47184173/article/details/111992933