Talk about the design and shortcomings of ThreadLocal

Students who develop in Java  ThreadLocal should not be unfamiliar with this class. There are many usage scenarios for this class, especially in some frameworks, such as database transaction operations, and data cross-layer transfer in the MVC framework. Here we briefly discuss  ThreadLocal the internal implementation and possible problems.

First ask yourself a question, how do you do it if you let yourself implement a functional class like this? The first reaction is to simply construct a  Map<Thread, T> data structure, the key is  Thread, and the value is the thread variable we want to save  T. Let's take a look at the problems with this design:

  • As the running time is longer, there are more Threads in the Map. When the Thread exits, the resources are not released, and there is a memory leak problem.
  • Because the Map data will be accessed by multiple threads and there is resource competition, it is necessary to perform synchronous and safe operations on the Map, which is inefficient.

The clever design in the JDK  ThreadLocal solves the above two problems. First of all, each Thread (thread) has a Map structure data inside ThreadLocalMap<ThreadLocal, T>. When we assign a value to a thread ThreadLocal.set(T value)variable , we actually first obtain Thread.currentThread())the internal attribute field of the current thread, and then set the thread variable value T ThreadLocalMapwith the current key. ThreadLocalThe essence of this design is that each Threadthread maintains a copy of its own ThreadLocalMapdata structure, which solves the second of the problems described above, with no race conditions.

Since each Threadinternally maintains a ThreadLocalMapdictionary data structure, the key value of the dictionary is ThreadLocal, when an ThreadLocalobject is no longer used (no other places are referenced), how does each thread that has been associated with it clear ThreadLocalit in its internal ThreadLocalMapWhat about this resource? The JDK ThreadLocalMaphas done another wonderful performance. It does not inherit java.util.Mapclasses, but implements a dictionary structure specially used to regularly clean up invalid resources. Its internal storage entity structure is Entry<ThreadLocal, T>inherited from java.lan.ref.WeakReference, so that when it ThreadLocalis no longer referenced, because of the weak reference mechanism, when the jvm finds that the memory is insufficient, it will automatically reclaim the instance memory pointed to by the weak reference, that is, the internal thread ThreadLocalMapwill release its ThreadLocalreference to it. So that the jvm recycles the ThreadLocalobject. It is emphasized here that the object is recycled ThreadLocal, not the whole Entry, so the value object in the thread variable Tstill exists in the memory, so the problem of memory leak has not been completely solved. Then analyze the implementation of the JDK, you will find that the invalid operation is periodically executed when calling ThreadLocal.get()or . So this solves problem 1 of the above problems.ThreadLocal.set(T)Entry

Is the problem really solved? It seems to be solved. Because there are no competing resource operations, there will be no memory leaks. But after thinking about it, I always feel that something is wrong. Is there really no memory overflow (OOM) problem? In the analysis of the above paragraph, it is emphasized that the internal invalid objects ThreadLocalMapwill be cleaned up regularly . The triggering condition is that it will be triggered when operations such as set, get, remove() are executed, but if there is such a scenario, when we execute it in a thread context A large internal data structure is set up, and then it should be cleared and reclaimed by reference, and the previous thread has been alive, then this large memory data object has not been reclaimed, and it is easy to write a code here to test the OOM. How to solve this problem?EntryTrheadLocalThreadLocal.set(T)ThreadLocalT

The classes in Lucene org.apache.lucene.util.CloseableThreadLocalsolve the problem caused by the above special scenario: that is, to solve the problem of invalid object recycling in the JDK because it is performed periodically. CloseableThreadLocalOne is maintained internally ThreadLocal. When executed CloseableThreadLocal.set(T), the internal is actually just a proxy to assign a value to the internal ThreadLocalobject, that is, to execute ThreadLocal.set(new WeakReference(T)). When you see this, you should understand that it is not directly stored here T, but it is packaged into a weak reference object. The purpose is that when the memory is insufficient, the jvm can recycle this object. But if you are careful, you will find that a new problem will be introduced, that is, when the current thread is still alive, the weak reference object is reclaimed due to insufficient memory, so that the get()value will not be returned to null when the next call is made, which is unacceptable. . Therefore CloseableThreadLocal, a data is also created internally WeakHashMap<Thread, T>. When the thread is alive, at least one reference of T exists, so it will not be recycled in advance. However, the second problem introduced is to limit WeakHashMapthe synchronization of the operation synchronized. You see, not everything is perfect, we just need to master that balance.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326059789&siteId=291194637