Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the implementation mechanism of ThreadLocal

2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article focuses on "how to understand the implementation mechanism of ThreadLocal", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to understand the implementation mechanism of ThreadLocal.

The following example demonstrates a typical application scenario for ThreadLocal. Before jdk 1.8, if we wanted to format the date and time, we needed to use the SimpleDateFormat class, which we knew was thread unsafe and had some strange problems with multithreaded concurrent execution. The best practice for using this class is to wrap it in ThreadLocal to ensure that each thread has its own SimpleDateFormat object, as follows:

ThreadLocal sdf = new ThreadLocal () {@ Override protected SimpleDateFormat initialValue () {return new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");}}; implementation mechanism

So how does ThreadLocal make it possible for decorated objects to hold a copy in each thread? Let's start with a brief summary from an overall point of view.

A static inner class ThreadLocalMap is defined in ThreadLocal, which can be understood as a unique Map type, while a threadLocals property of ThreadLocalMap type is declared in the Thread class. For each Thread object, that is, each thread contains a ThreadLocalMap object, that is, each thread has its own in-memory database, and what is stored in the database is the object that we decorate with ThreadLocal. The whole process is a bit circuitous, which can be understood with the help of the following picture:

Here key is the corresponding ThreadLocal object itself, and value is the property value modified by ThreadLocal. When we want to get this object, we first need to get the Thread object corresponding to the current thread, then get the corresponding threadLocals property of the object, that is, get the thread's private in-memory database, and finally use the ThreadLocal object as the key to get the modified target value.

Thread memory database

Next, let's take a look at the corresponding source code implementation. First, let's take a look at the internally defined ThreadLocalMap static inner class:

Static class ThreadLocalMap {/ / weakly referenced key, inherited from WeakReference static class Entry extends WeakReference > {/ * * The value associated with this ThreadLocal. * / Object value; Entry (ThreadLocal k, Object v) {super (k); value = v;}}

This makes the key a weak reference. We know that the weak reference object has a very short life cycle. When the garbage collector thread scans the memory area under its jurisdiction, once the weak reference object is found, its memory will be reclaimed regardless of whether the current memory space is sufficient or not. In other words, such a design can easily lead to the collection of ThreadLocal objects, the length of time for which the thread executes tasks is not fixed, and it is convenient for the garbage collector to collect thread-private variables.

From this we can see that the purpose of the author's design is to prevent memory leaks, so how has it become the fuse of memory leaks analyzed by many articles? The common point of these articles is that key is recycled, but value is a strong reference that is not recycled, and these value become zombies. There is nothing wrong with this analysis. Value does exist and is in the same life cycle as threads, but the following strategy ensures that memory leaks are avoided as much as possible:

ThreadLocal cleans up the value value whose key is null every time it performs get and set operations.

Value has the same life cycle as the thread. When the thread dies, it is also the day when the value is GC.

Strategy 1 has nothing to say, just look at the source code. Let's give an example to verify strategy 2:

Public class ThreadLocalWithMemoryLeak implements Callable {private class My50MB {private byte [] buffer = new byte [50 * 1024 * 1024]; @ Override protected void finalize () throws Throwable {super.finalize (); System.out.println ("gc my 50 mb");}} private class MyThreadLocal extends ThreadLocal {@ Override protected void finalize () throws Throwable {super.finalize () System.out.println ("gc my thread local");}} private MyThreadLocal threadLocal = new MyThreadLocal (); @ Override public Boolean call () throws Exception {System.out.println ("Thread-" + Thread.currentThread (). GetId () + "is running"); threadLocal.set (new My50MB ()); threadLocal = null; return true } public static void main (String [] args) throws Exception {ExecutorService es = Executors.newCachedThreadPool (); Future future = es.submit (new ThreadLocalWithMemoryLeak ()); future.get (); / / gc my thread local System.out.println ("do gc"); System.gc (); TimeUnit.SECONDS.sleep (1); / / sleep 60s System.out.println ("sleep 60s") TimeUnit.SECONDS.sleep (60); / / gc my 50 mb System.out.println ("do gc"); System.gc (); es.shutdown ();}}

The final output of the above program is as follows:

Thread-11 is runningdo gcgc my thread localsleep 60sdo gcgc my 50 mb

You can see that the value is finally GC, although the first GC is not recycled, which also verifies that the value and the thread have the same life cycle. The reason for waiting for 60 seconds in the example is that the default life cycle of the thread in Executors#newCachedThreadPool is 60 seconds. If the thread is not reused during the life cycle, it will die. Here, we are waiting for the thread to die. Once the thread dies, the value will be GC.

So the premise of a memory leak must be that the thread holding the value is alive all the time, which is normal when using the thread pool, in which case the value will never be GC, because the thread object maintains a strong reference to the value. In addition, the business executed by subsequent threads has not called the get or set method of ThreadLocal, resulting in not taking the initiative to delete the value object whose key is null. Under these two conditions, the value object is always resident in memory, so there is the possibility of memory leakage.

So how should we avoid it? We analyzed earlier that there is a small landmine when using ThreadLocal in the case of thread pool, and memory leaks here usually occur in the case of thread pool, so when using ThreadLocal, take the initiative to call the remove method to clear the value that is no longer valid, so as to eliminate hidden dangers, this is also a best practice.

What is InheritableThreadLocal?

InheritableThreadLocal inherits from ThreadLocal, and its implementation is relatively simple (as follows), so what's the difference between InheritableThreadLocal and ThreadLocal?

Public class InheritableThreadLocal extends ThreadLocal {@ Override protected T childValue (T parentValue) {return parentValue;} @ Override ThreadLocalMap getMap (Thread t) {return t.ThreadLocals;} @ Override void createMap (Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap (this, firstValue);}}

Before we start the analysis, let's demonstrate a case of ThreadLocal, as follows:

Private static ThreadLocal tl = new ThreadLocal (); public static void main (String [] args) {tl.set ("zhenchao"); System.out.println ("Main thread:" + tl.get ()); Thread thread = new Thread (()-> System.out.println ("Sub thread:" + tl.get ()); thread.start ();}

Run the above example and the output is as follows:

Main thread: zhenchaoSub thread: null

You can see that the child thread cannot get the ThreadLocal variable set by the main thread, which is understandable. After all, there are still two threads between the main thread and the child thread, but in some scenarios we want the ThreadLocal variable to be inherited for the relationship between the main thread and the child thread. At this time, you can use InheritableThreadLocal to implement it. For the above example, you only need to change ThreadLocal to InheritableThreadLocal. The specific implementation is relatively simple, and readers can try it themselves.

Let's analyze how InheritableThreadLocal allows ThreadLocal variables to inherit between the main thread and the child thread. From the point of view of the implementation of InheritableThreadLocal, InheritableThreadLocal replaces ThreadLocal's threadLocals variable with the inheritableThreadLocals variable, and both variables are of type ThreadLocalMap. During initialization, the child thread determines whether the inheritableThreadLocals of the parent thread is null. If it is not null, initialize its own inheritableThreadLocals with the inheritableThreadLocals variable of the parent class. The implementation is as follows (in the Thread#init method):

/ / if the inheritableThreadLocals variable of the parent thread is not empty, copy it to the child thread if (inheritThreadLocals & & parent.inheritableThreadLocals! = null) {this.inheritableThreadLocals = ThreadLocal.createInheritedMap (parent.inheritableThreadLocals);}

The implementation of ThreadLocal#createInheritedMap is as follows:

Static ThreadLocalMap createInheritedMap (ThreadLocalMap parentMap) {return new ThreadLocalMap (parentMap);} private ThreadLocalMap (ThreadLocalMap parentMap) {Entry [] parentTable = parentMap.table; int len = parentTable.length; setThreshold (len); table = new Entry [len]; for (int j = 0; j < len; jacks +) {Entry e = parentTable [j] If (e! = null) {@ SuppressWarnings ("unchecked") ThreadLocal key = (ThreadLocal) e.get (); if (key! = null) {/ / call InheritableThreadLocal's childValue method Object value = key.childValue (e.value); Entry c = new Entry (key, value) Int h = key.threadLocalHashCode & (len-1); while (table [h]! = null) {h = nextIndex (h, len);} table [h] = c; size++;}

The implementation of the method InheritableThreadLocal#childValue simply returns the value in the parent thread, so the above process is essentially a process of copying the value of the ThreadLocal variable in the parent thread.

At this point, I believe you have a deeper understanding of "how to understand the implementation mechanism of ThreadLocal". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report