ThreadLocal, Thread and ThreadLocalMap share
Introduction
The usefulness of ThreadLocal
ThreadLocal is to record a copy of the data to a thread to ensure the thread safety of the data
For example, the Connection of the database is put into ThreadLocal, a transaction will use a lot of DAO, but only a common Connection can be used, so as to ensure the integrity of the transaction
So when one of the variables of a certain class will be used multiple times by the same thread, and it is strictly stipulated that this variable must be operated every time
Then you can put this variable into ThreadLocal
Spring also puts various beans into ThreadLocal to ensure the "stateless" of beans
Don't be misled
Today I read the introduction about ThreadLocal, and some blogs about ThreadLocal on the Internet. This principle introduction is really a pit, a big mistake
You may have seen the following simple implementation ideas of the so-called ThreadLocal:
After that, I added:
Although the ThreadLocal implementation version in the above code listing is relatively simple and crude, its purpose is mainly to present the implementation ideas of the ThreadLocal class provided in the JDK
The above simplified ThreadLocal implementation is fundamentally wrong.
Not only in some blogs, but also in books. Spreading knowledge to others can indeed simplify the code implementation, but it does not mean that the correct implementation ideas have been changed!
This will mislead others to further study ThreadLocal
The real implementation of ThreadLocal
Let’s talk about the summary first and compare it with the above mistakes
1. ThreadLocalMap Don't look at the end of a Map, in fact, it is a reimplemented class
It has nothing to do with Map. If the Map interface is not implemented, HashMap is useless. Don't think that you can only use map to find the value according to the key.
2. The thread finds the corresponding value according to the key. This key is not the thread id, but the ThreadLocal class
Why, because ThreadLocalMap is stored in threads, each thread has only one ThreadLocalMap that belongs to itself
In this case, what's the point of saving a hairy thread id?
Uncover the real relationship between Thread, ThreadLocal, ThreadLocalMap
First enter ThreadLocal and find the following
ThreadLocalMap is actually implemented in ThreadLocal
Find the set() method of ThreadLocal directly
1 public void set(T value) { 2 Thread t = Thread.currentThread(); // Get the current thread 3 ThreadLocalMap map = getMap(t); // Pass the current thread as a parameter to get ThreadLocalMap 4 if (map != null ) 5 map.set( this , value); 6 else 7 createMap(t, value); 8 }
Then enter the getMap() method
1 ThreadLocalMap getMap(Thread t) { 2 return t.threadLocals; // After tracing, we found that t.threadLocals is ThreadLocal.ThreadLocalMap threadLocals; 3 }
If the obtained map is null, it means it is the first time, go to createMap method to create
1 void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap( this , firstValue); // See, ThreadLocalMap is directly saved for Thread3 }
Enter the new ThreadLocalMap method, note here that the incoming this refers to ThreadLocal
1 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { // See the parameter name, firstKey, ThreadLocal is passed in as the key value! 2 table = new Entry[INITIAL_CAPACITY]; // table is actually private Entry[] table; just an array 3 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1 ); 4 table[i] = new Entry(firstKey, firstValue) ; 5 size = 1 ; 6 setThreshold(INITIAL_CAPACITY); 7 }
ThreadLocalMap does not implement the Map interface and has nothing to do with Map. As for what Entry is, let's see
1 static class Entry extends WeakReference<ThreadLocal<?>> { 2 /** The value associated with this ThreadLocal. */ 3 Object value; 4 5 Entry(ThreadLocal<?> k, Object v) { 6 super (k); 7 value = v; 8 } 9 }
First of all, WeakReference refers to the meaning of weak reference. Inheriting this thing has the following effects:
When an object is only pointed by weak reference (weak reference) without any other strong reference (strong reference), if the GC runs at this time, then the object will be reclaimed, regardless of whether the current memory space is enough, this Objects are recycled.
The advantage is that if a ThreadLocal is recycled, the key of ThreadLocalMap will also be recycled in the next GC() (otherwise it will cause memory leak, and the key will never be called)
Entry是一个ThreadLocalMap的内部类,跟Map的Entry实现有点像,有key和value的定义(俗称 桶)
ok,我们发现,ThreadLocalMap创建后,就是初始化一个Entry的数组,生成一个Entry将ThreadLocal作为key值,将要存的值作为value放入其中存好
那么假设现在ThreadLocalMap已经存在,走的是第一个分支,直接set,我们看看他怎么实现的
1 private void set(ThreadLocal<?> key, Object value) { 2 3 Entry[] tab = table; 4 int len = tab.length; 5 int i = key.threadLocalHashCode & (len-1 ); 6 7 for ( Entry e = tab[i]; 8 e != null ; 9 e = tab[i = nextIndex(i, len)]) { 10 ThreadLocal<?> k = e.get(); 11 12 if (k == key) { 13 e.value = value; 14 return ; 15 } 16 17 if (k == null ) { 18 replaceStaleEntry(key, value, i); 19 return ; 20 } 21 } 22 23 tab[i] = new Entry(key, value); 24 int sz = ++ size; 25 if (!cleanSomeSlots(i, sz) && sz >= threshold) 26 rehash(); 27 }
Seeing that there is no for in line 7, directly traverse the Entry array just mentioned, take out the ThreadLocal for comparison (equivalent to key comparison), set the value if it matches, and save it without this key
Why ThreadLocalMap does not use HashMap but implements its own array
The concept of key and value appears, and it is not necessary to use HashMap, why use an array
Personally, I think it saves overhead, the overhead of creating Map objects and the overhead of using Map. After all, the initial default length of ThreadLocalMap is 16, and in reality, a thread will not have such local variables to save
Therefore, when using ThreadLocal to store, ThreadLocal will create a ThreadLocalMap for the thread that calls it, and save it as the key value.
When taking the value, ThreadLocal takes the ThreadLocal saved in Thread, and then uses itself as the key value to take the value
Why is the code of ThreadLocalMap not placed in Thread
There is an answer on the Internet that I think is very reasonable:
It seems more logical to define ThreadLocalMap inside the Thread classBut ThreadLocalMap does not require a Thread object to operate, so defining it in the Thread class will only add some unnecessary overhead.The reason it is defined in the ThreadLocal class is that the ThreadLocal class is responsible for the creation and use of ThreadLocalMapIn general, ThreadLocalMap is not a necessity, the definition in Thread increases the cost, and the definition is created on demand in ThreadLocal.
Threads may not all use ThreadLocal. If they are not used, they will not be assigned a ThreadLocalMap.
In other words, this is actually a good design pattern:
A tool class regards itself as a unique identifier, and to operate the method of the tool class itself, it only needs to record the data to the class that calls it.
ThreadLocal memory leak problem
Normally, we create a thread, which will be destroyed after running, and automatically call the remove() method of ThreadLocal to clear the contents of ThreadLocalMap
However, in practice, we use the thread pool, and the thread will return to the thread pool after running, and will not be destroyed
At this time, the content of ThreadLocalMap is still there (the memory is leaked here)
Therefore, when using ThreadLocal in the thread pool, remember to use the remove() method to clear the key in ThreadLocalMap when run() is finished.
0 Comments