当前位置:网站首页>Detailed explanation of ThreadLocal

Detailed explanation of ThreadLocal

2022-06-27 01:47:00 ZNineSun

1.ThreadLocal brief introduction

stay java In the thread , Each thread has one ThreadLocalMap Instance variables ( If not used ThreadLocal, Will not create this Map, The first time a thread accesses a ThreadLocal variable , Will create ).

ThreadLocal It's easy to be literal , Take it for granted that it's a “ Local thread ”. Actually ,ThreadLocal Not one Thread, It is Thread Local variables of , Maybe name it ThreadLocalVariable It's easier to understand .

It mainly consists of four methods initialValue(),get(),set(T),remove(), Among them, it is worth noting that initialValue(), The method is a protected Methods , Obviously it's specifically implemented for subclass rewriting . This method returns the initial value of the current thread's local variable in this thread , This method is a delay call method , In a thread 1 Secondary call get() Execution only , And only execute 1 Time ( namely : Call this method at most once each time you access a thread to get a local variable for each thread , The first time a thread uses get() Method to access variables . If the thread precedes get Method call set(T) Method , Will not be called again in the thread initialValue Method ).ThreadLocal The default implementation in returns a null:

The Map It is solved by linear detection hash The question of conflict , If you don't find free slot, Just keep trying back , Until you find a free place , Insert entry, This way is often encountered in hash When the conflict , Affect efficiency .
Let's discuss it in detail ThreadLocal.

2.ThreadLocal The role of

ThreadLocal The main function of this system is to isolate data ,ThreadLocal The variables filled in belong to the current thread , This variable is isolated from other threads , That is, this variable is unique to the current thread .
ThreadLocal A copy of the variable is created in each thread , Then each thread can access its own internal copy variables .
 Insert picture description here

When it comes to isolation , It should not be difficult for us to get in touch with Isolation of transactions , you 're right ,Spring To realize transaction isolation, we use Threadlocal The way , To ensure that database operations in a single thread use the same database connection , meanwhile , In this way, the business layer does not need to sense and manage the transaction when using it connection object , Through the propagation level (@Transaction), Skillfully manage switching between multiple transaction configurations , Suspend and resume .

Spring It's used in the frame ThreadLocal To achieve this isolation , Mainly in the TransactionSynchronizationManager In this class :
 Insert picture description here

It's worth noting that :Spring The main business is ThreadLocal and AOP To do what is realized

besides , We are using SimpleDataFormat It also uses , Maybe you're using SimpleDataFormat It's just simple new One. SimpleDataFormat object , But when we use SimpleDataFormat Of parse() When the method is used , The method has a Calendar object , call SimpleDataFormat Of parse() Method will call first Calendar.clear(), And then call Calendar.add(), If a thread calls first add() Then another thread calls clear(), Now parse() The time to parse the method is wrong .

The easiest way to solve this problem is to make every thread new One's own SimpleDataFormat Just fine , But the big problem is if we have 1000 It's a thread new1000 individual SimpleDataFormat?

So we can use the thread pool to add ThreadLocal packing SimpleDataFormat, Call again initialValue Let each thread have a SimpleDataFormat Copy of , This solves the problem of thread safety , It also improves performance .

You think there are only so many places to use threadLocal??? take it easy , If a thread exists in a project, it often encounters calls across several methods , The object that needs to be passed , That is context (Context), It's a state , Often it's user identity 、 Mission information, etc , There will be the problem of transitional transmission .

If we use a similar responsibility chain model , Add one to each method context The parameters are very cumbersome , And sometimes , If the call chain has a third-party library that cannot modify the source code , Object parameters will not be passed in , So we use ThreadLocal I made a little transformation , This only needs to be done before the call ThreadLocal Setting parameters in , other place get Just a moment , Just like below :
 Insert picture description here

meanwhile , Like we often use cookie,session Data isolation is done through ThreadLocal To do what is realized .

I also mentioned above ThreadLocal It is mainly used for data isolation , So it's with Synchronized What's the difference ?

3.ThreadLocal And Synchronized The difference between

ThreadLocal In fact, it is a variable bound to the thread .ThreadLocal and Synchonized Are used to solve multithreaded concurrent access .

however ThreadLocal And synchronized There are essential differences :
1、Synchronized For data sharing between threads , and ThreadLocal It is used for data isolation between threads .

2、Synchronized It's using the lock mechanism , Make variables or code blocks accessible to only one thread at a time .
and ThreadLocal A copy of the variable is provided for each thread , So that each thread at a certain time access is not the same object , This isolates multiple threads from sharing data .
and Synchronized But the opposite is true , It is used to obtain data sharing when communicating among multiple threads .

Simply speaking ThreadLocal,threadlocl Is a property in the current thread ThreadLocalMap One of the sets Entry Of key value Entry(threadlocl,value), Although different threads threadlocal This key The value is the same , But what different threads have ThreadLocalMap It's unique , That is, different threads share the same ThreadLocal(key) Corresponding to the stored value (value) Dissimilarity , Thus, the purpose of variable isolation between threads is achieved , But in the same thread, this value The variable address is the same .

4.ThreadLocal The underlying implementation of

ThreadLocal<String> localName = new ThreadLocal();
localName.set(" Zhang San ");
String name = localName.get();
localName.remove();

The code above is very simple : stay ThreadLocal Store an element , Then go to get it, and finally remove this element , As a whole ThreadLocal Just these three basic operations :
set、get、remove

Let's take a look at the source code in turn :

4.1 set

 Insert picture description here

set It's very simple : Mainly ThreadLocalMap We need to focus on , and ThreadLocalMap The current thread Thread One is called threadLocals From the variable of .
 Insert picture description here

See here , We have actually discovered ThreadLocal The truth about data isolation .

Every thread Thread They all maintain their own threadLocals Variable , So create in each thread ThreadLocal When , In fact, data exists in its own thread Thread Of threadLocals In variables , No one else can get it , So isolation is achieved .

I mentioned one above ThreadLocalMap,ThreadLocalMap What does the underlying structure look like ?

4.2 ThreadLocalMap

 Insert picture description here

Let's take a look at the source code shown in the figure above ,
Since there is a Map So his data structure is very similar to HashMap Of , But look at the source code can find , It didn't come true Map Interface , And his Entry It's inheritance WeakReference( Weak reference ) Of , I didn't see HashMap Medium next, So there's no linked list .

Let me briefly explain weak references : Weak references are mainly used in cases where they are not blocked key perhaps value Recycled mapping, What does that mean ? Weak references are created for garbage collection services . It refers to an object , But it does not prevent the object from being recycled . If you use a strong reference , As long as the reference exists , Then the referenced object cannot be recycled . Weak references don't have this problem . While the garbage collector is running , If all references to an object are weak references , The object will be recycled .

 Insert picture description here

There's a problem , How to solve the problem without the linked list Hash What about the conflict ?

threadlocalmap Structure is entry Array , We can have more than one thread in the development process TreadLocal To store different types of objects , But they will all be put into your current thread ThreadLocalMap in , So you have to store arrays .
As for the specific solution hash Conflicting , Let's talk about the source code first :
 Insert picture description here

See from the source code ThreadLocalMap When it's stored, it's going to give each one ThreadLocal Object to one threadLocalHashCode, During insertion , according to ThreadLocal Object's hash value , Locate the table Position in i,int i = key.threadLocalHashCode & (len-1).
Obviously this is a very simple linear detection method , So solve it hash The way of conflict is Linear detection method
And then we will judge : If the current position is empty , Just initialize one Entry Put the object in position i On .
 Insert picture description here

If the position i Not empty , If this Entry Object's key It's just about to be set up key, So refresh Entry Medium value;
 Insert picture description here

If the position i Is not empty , and key It's not equal to entry, Then find the next empty position , Until it's empty .
The overall process is shown in the figure below :
 Insert picture description here

thus , stay get When , According to ThreadLocal Object's hash value , Locate the table Position in , Then judge the position Entry Object key Whether and get Of key Agreement , If it's not consistent , Just judge the next position ,set and get If the conflict is serious , The efficiency is still very low .

4.3 get

 Insert picture description here

This is the picture above get All processes of

Speaking of this, many people may be thinking ThreadLocal Where are instances of and their values stored ?

stay Java in , Stack memory belongs to a single thread , Each thread will have a stack memory , The stored variables can only be seen in the thread to which they belong , That is, stack memory can be understood as thread's private memory , Objects in the heap memory are visible to all threads , Objects in heap memory can be accessed by all threads .

But it can't be said that ThreadLocal The instance of and its value are stored on the stack , although threadLocal The median value is private to each thread ,ThreadLocal An instance is actually held by the class it creates ( The top should be held by the thread ), and ThreadLocal The value of is actually held by the thread instance , They're all on the pile , We just changed the visibility to thread visibility through some tricks .

5. Shared thread ThreadLocal data

Use InheritableThreadLocal It can be accessed by multiple threads ThreadLocal Value , We create a... In the main thread InheritableThreadLocal Example , And then get this in the child thread InheritableThreadLocal Instance settings .

    public void test() {
    
        final ThreadLocal threadLocal = new InheritableThreadLocal();
        threadLocal.set("ninesun");
        Thread t = new Thread() {
    
            @Override
            public void run() {
    
                super.run();
                System.out.println(" Get stored values :" + threadLocal.get());
            }
        };
        t.start();
    }

Okay , We now know how to use InheritableThreadLocal It can be accessed by multiple threads ThreadLocal Value , But how are these values passed between child threads ?
The logic of transmission is simple ,
 Insert picture description here

The picture above is what I intercepted Thead Inside the code snippet ,thread When initializing the creation ( In the constructor ) There are the following operations :
 Insert picture description here

This code is also very simple , Roughly speaking : If the thread's inheritThreadLocals Variable is not empty , And the parent thread's inheritThreadLocals Also exist , So I'll take the parent thread's inheritThreadLocals For the current thread inheritThreadLocals. Let's take our example above .

ThreadLocal I've been talking about it for the most part , But you may not realize the seriousness of the problem , Because it said ,key Is a weak reference , and value But strong citation , If we're using threadLocal Improper operation , It will lead to a very serious consequence : Memory leak

6. Memory leak

 Insert picture description here

We can see ,ThreadLocal When you save it, you think of yourself as Key There is ThreadLocalMap in , The normal situation should be key and value Should be strongly quoted by the outside world , But now key Designed to WeakReference A weak reference to .
 Insert picture description here

This is the picture above key By GC Later scenes .
The reason for the above scenario comes from The life cycle of a weakly referenced object

Objects with only weak references have a shorter life cycle , In the process of the garbage collector thread scanning the memory area it governs , Once you find an object with only weak references , Whether the current memory space is enough or not , Will reclaim its memory .
however , Because the garbage collector is a low priority thread , So it's not necessarily easy to find objects with only weak references .

This leads to a problem ,ThreadLocal When there is no external strong reference , happen GC Will be recycled , If you create ThreadLocal The thread of has been running , So this Entry Object value It's possible that they won't be recycled , Memory leak .

This may not be very intuitive , Let me give you a simple example :

For example, the threads in the thread pool , Threads are reusable , After the previous thread instance has been processed , Threads are still alive for reuse purposes , therefore ,ThreadLocal Set value Value is held , Causing a memory leak .

How can we solve it ?
The solution is too simple , At the end of the code remove Just fine , We just need to remember to use at the end of use remove Just clear the value .
such as , Our previous code is :

    public void test() {
    
        final ThreadLocal threadLocal = new InheritableThreadLocal();
        threadLocal.set("ninesun");
    }

Then we can go through :

    public void test() {
    
        final ThreadLocal threadLocal = new InheritableThreadLocal();
        try {
    
            threadLocal.set("ninesun");
        } finally {
    
            threadLocal.remove();
        }

    }

 Insert picture description here

remove The source code is also very simple , As shown in the figure above , Is to find the corresponding values and set them to blank , So when the garbage collector is recycling , They're automatically recycled .

So here comes the question , Why do we have to key Designed as weak reference ?

7.ThreadLocalMap Of key Design it as a weak reference ?

key If it is not set to weak reference, it will cause and entry in value The same memory leak scenario .

If threadlocalmap Of key Is a strong quote , So as long as the thread exists , threadlocalmap There is a , and threadlocalmap Structure is entry Array . That is, the corresponding entry Arrays exist , and entry Of array elements key yes threadLocal.

Even if we explicitly assign values in the code threadlocal by null, tell gc To garbage collect this object . Due to the strong reference above , threadlocal Even if the assignment is null, As long as the thread exists , threadlocal It's not recycled .

And set to weak reference , gc When the scan arrives , Find out threadlocal No strong references , Will recycle the threadlocal object .

also threadlocal Of set get remove Will judge whether key by null, If null, that value Will also be removed , After that will be gc Recycling .
 Insert picture description here

ThreadLocal Deficiency , Such as using the least efficient linear detection method to solve conflicts , You can see netty Of fastThreadLocal To make up for .《 Talk about FastLocal Why so fast 》

原网站

版权声明
本文为[ZNineSun]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/178/202206270130428624.html