ThreadLocal is a “thread-closed” mechanism provided by Java that creates a copy of a variable for each thread to keep it thread-safe.
Its purpose is entirely at odds with synchronized. Synchronized ensures the consistency of data in multi-threaded environment, while ThreadLocal ensures the independence of data in multi-threaded environment.
There are two ways to construct a ThreadLocal object.
The first way is directly through new, as follows:
ThreadLocal<String> t = new ThreadLocal();
Copy the code
Or it can be constructed using the static ThreadLocal#withInitial(Suppiler) method:
ThreadLocal<String> t = ThreadLocal.withInitial(() -> "initial value");
Copy the code
In this way, you actually construct a SuppliedThreadLocal object. This class is a subclass of ThreadLocal. Internally, the suppiler function passed in by the static method is used to allow the caller to get an initialized value without ever setting ****value**.
Once ThreadLocal is built, you can manipulate the corresponding values by using set() and get() and remove().
t.set("another value"); // Set ThreadLocal to String value = t.set (); // get ThreadLocal value t.emove (); // Delete the value of ThreadLocalCopy the code
The inner workings of ThreadLocal
In each Thread objects, there is a type of ThreadLocal. ThreadLocalMap objects, called threadLocals. This is where ThreadLocal values are actually stored, and this field is maintained by the ThreadLocal, not managed by the Thread object itself.
ThreadLocalMap maintains an Entry type of data internally called table. Entry is a static inner class of ThreadLocalMap with the following class definition:
static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}Copy the code
Entry is a class of K:V structure, where Key is a ThreadLocal object, a WeakReference object, and Value is the Value set by the user.
Based on the above information, the overall structure of ThreadLocal is as follows:
Memory leak risk
In ThreadLocal’s underlying storage structure, ThreadLocalMap.entry, the ThreadLocal object exists as a Key and as a weak reference.
Weak references have the following features:
Used with the WeakReference class, WeakReference objects are reclaimed at the next garbage collection in the absence of other strong reference relationships
As you can see from the above figure, only the Defined Class (Defined by the developer, which instantiates ThreadLocal) objects have direct strong references to ThreadLocal objects. That is, when Defined Class instances are reclaimed by the GC, the memory space of the Key in ThreadLocalMap.Entry can easily be reclaimed, resulting in null values.
Modern applications make extensive use of pooling techniques to reuse threads, and thread objects cannot necessarily be recycled. In this case, the Value has a strong reference chain of “Thread — > TheadLocalMap — > ThreadLocalmap.entry — > Value” and cannot be reclaimed. But in the case of its Key being reclaimed, the developer can never access the Value, resulting in a memory leak.
Memory leak solution
There is a method called cleanSomeSlots in ThreadLocalMap that scans for entries with a null Key and deletes them.
This method is called when ThreadLocal’s get(), set(Object), and remove() methods are called. Therefore, in most scenarios, ThreadLocal memory leaks are already relatively rare.
But there are two ways we can “strengthen our defenses.”
The first approach considers making ThreadLocal objects static.
public class Main {
public static ThreadLocal<String> t = new ThreadLocal();
}
Copy the code
At this point, ThreadLocal and Class objects are strongly referenced, and ThreadLocal objects are not recycled unless “type unload” occurs.
In this way, the keys and values of threadLocalMap. Entry are not reclaimed and can be accessed by the specified thread. But the drawbacks are also obvious. If ThreadLocal stores data that is intended for temporary use, “never recycled” keys and values are in effect another kind of “memory leak.”
A better way is to recycle manually, which is to manually call the ThreadLocal#remove() method after use.
ThreadLocal<String> t = new ThreadLocal<>()
try {
t.set(obj);
doSomething();
} finally {
t.remove();
}
Copy the code
This approach, while inflexible, is by far the most recommended way to use ThreadLocal.