ThreadLocal simple analysis from the source angle

Source: Internet
Author: User
Tags rehash static class java reference

Directory

    • Analysis of Threadlcoal Source code
    • Garbage collection of Threadlocal
      • Java reference
      • Recycling of Threadlocal
      • Recycling of Threadlocalmap in each thread
      • Memory leak issues
    • Summarize
    • Reference
Analysis of Threadlcoal Source code

We know that threadlocal is used to maintain multiple thread-thread- independent copies of variables that are shared only within threads , across methods, classes, and so on, a threadlocal that maintains multiple thread integer variables:

ThreadLocal<Integer> threadLocalNum = new ThreadLocal<>();

Each used threadLocalNum thread can create a separate copy of the variable in a form like threadLocalNum.set(1) this, Integer so how does it work? Let's have a brief analysis today.

First look at the Threadlocal set method is how to achieve, the source code is as follows:

public void set(T value) {        Thread t = Thread.currentThread();  //获取当前线程        ThreadLocalMap map = getMap(t);     //获取当前线程的ThreadLocalMap        if (map != null)            map.set(this, value);           //当前线程的ThreadLocalMap不为空则直接设值        else            createMap(t, value);            //当前线程的ThreadLocalMap为空则创建一个来设置值    }

Yes, you're not mistaken, it's getting the value set in the current thread ThreadLocalMap , let's look at getMap(t) how it's implemented:

ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }

Then we see that the thread contains an attribute of type Threadlocalmap:

ThreadLocal.ThreadLocalMap threadLocals = null;

Here we can conclude that each thread holds a Threadlocalmap property, which is set directly to the Threadlocalmap property of the corresponding thread when the variable is set by Threadlocal .

So how does the value set by threadlocal in different threads correlate with the defined threadlocal variable and threadlocalmap in thread? We went on to analyze.

The previous write to the current thread of Threadlocalmap is empty then create a threadlocalmap to set the value , we look createMap(t, value) at the specific implementation:

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }/////////////////////ThreadLocalMap构造器定义如下ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {            table = new Entry[INITIAL_CAPACITY];            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);  //            table[i] = new Entry(firstKey, firstValue);            size = 1;            setThreshold(INITIAL_CAPACITY);        }private static final int INITIAL_CAPACITY = 16;

The threadlocals in the thread is a THREADLOCALMAP variable whose default value is null, which is instantiated by the first time the Threadlocal object is invoked with set createMap(Thread t, T firstValue) .

Let's take a look at Threadlocalmap, which is a static inner class defined in threadlocal with the following properties:

        /**         * The initial capacity -- MUST be a power of two.         */        private static final int INITIAL_CAPACITY = 16;        /**         * The table, resized as necessary.         * table.length MUST always be a power of two.         */        private Entry[] table;        /**         * The number of entries in the table.         */        private int size = 0;        /**         * The next size value at which to resize.         */        private int threshold; // Default to 0

Where private Entry[] table the property is used to store variables that come in through threadlocal set, entry is defined as follows:

static class Entry extends WeakReference<ThreadLocal<?>> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal<?> k, Object v) {                super(k);                value = v;            }        }

EntryInherited WeakReference<ThreadLocal<?>> , threadlocal is specified as a weak reference in the constructor super(k) (this is followed by a separate discussion of why weak references are used here).

At this point, we can know the memory structure of threadlocal and thead as follows:

Garbage collection of Threadlocal

Online to see a lot of articles are talking about threadlocal memory leak problem, so also here to briefly explain their understanding.

From the structure above, it can be seen that the objects to be recycled include: threadlocal

    • Threadlocal instance itself
    • Threadlocalmap in each thread, including key for each entry, value

The following is a brief description of the Java reference, and then separately discusses the threadlocal itself and Threadlcoalmap recycling

Java reference
    • Strong reference (Strongreference): object can not be reclaimed by GC, space shortage times error
    • Soft references (SoftReference): objects have no other strong references and are recycled by GC when there is not enough space.
    • Weak references (WeakReference): objects have no other strong references, and GC processes are recycled when they are scanned.
Recycling of Threadlocal

Threadlocal instances are referenced in two main ways:

    • Strong references at the threadlocal definition
    • Key=weak (threadLocal) in Threadlocalmap in each thread, is a weak reference

In the case of a strong reference, threadlocal must not be recycled; After a strong reference, because the entry key in each thread is a weak reference, it becomes NULL after the next GC. When a threadlocal instance is recycled depends entirely on when a strong reference is killed, and when does a strong reference be destroyed? The simplest is that a threadLocal=null strong reference is assigned a value of NULL, and the other is threadlocal is a local variable, the reference is destroyed after the method exits, and so on.

Here's an answer to the earlier question of why the key is designed as a weak reference in Threadlocalmap , and what happens if a strong reference is present in Threadlocalmap? When a strong reference defined at Threadlocal is set to NULL, if there are other threads that use the threadlocal that are not completed and will take a long time to complete, the thread will hold a reference to the threadlocal instance until the thread finishes, While threadlocal instances cannot be recycled, the most important thing is that if you do not understand the threadlocal internal implementation, you may not know that there are other threads referencing the threadlocal instance.

The code to clear Threadlocalmap at the end of the thread is Thread.exit() as follows:

   /**     * This method is called by the system to give a Thread     * a chance to clean up before it actually exits.     */    private void exit() {        if (group != null) {            group.threadTerminated(this);            group = null;        }        /* Aggressively null out all reference fields: see bug 4006245 */        target = null;        /* Speed the release of some of these resources */        threadLocals = null;        inheritableThreadLocals = null;        inheritedAccessControlContext = null;        blocker = null;        uncaughtExceptionHandler = null;    }
Recycling of Threadlocalmap in each thread

From the point of reference, the Threadlocalmap in each thread, including the key and value of each entry, the thread (that is, the thread instance) itself holds a strong reference to the Threadlocalmap, which is recycled only at the end of the line. But Threadlocal provides some ways to implement it:set/get/remove, you can reclaim other entry instances that have been invalidated (key=null) when they are executed.

Here we take set as an example to see how ThreadLocal recycles entry, and the ThreadLocal set method is implemented as follows:

threadlocalpublic void Set (T value) {Thread T = Thread.CurrentThread ();        Threadlocalmap map = getmap (t);  if (map! = null) Map.set (this, value);   The method of this secondary analysis is else Createmap (t, value); The}//threadlocalmapprivate void set (threadlocal<?> key, Object value) {//We don ' t use a FA is already analyzed here St path As with get () because it was at//least as common to use Set () to create new entries AS//I            Existing ones, in which case, a fast//path would fail more often than not.            entry[] tab = table;            int len = tab.length;  int i = Key.threadlocalhashcode & (len-1);  Gets the hashcode of the current threadlocal instance, and is also the subscript for the table//here for the Loop key, because the hash conflict causes the hashcode to point to the subscript is not the actual storage location for (Entry                 e = Tab[i];                 E! = null;                E = Tab[i = Nextindex (i, Len)]) {threadlocal<?> k = E.get ();    Found the setting to new value            if (k = = key) {E.value = value;                Return                    }//entry is not null,key null//Description was originally assigned, but the original threadlocal has been reclaimed if (k = = null) {                    Replacestaleentry (key, value, I);                Return            }}//If the subscript corresponding to Entry is NULL, create a new Entry tab[i] = new Entry (key, value);            int sz = ++size;                Clean up other recycled entry in threadlocal (i.e. Key=null entry) if (!cleansomeslots (i, SZ) && sz >= threshold)        Rehash rehash (); }

Take a look at the implementation of Cleansomeslots:

Threadlocalmapprivate boolean cleansomeslots (int i, int n) {Boolean removed = false;            entry[] tab = table;            int len = tab.length;                Do {//get subscript i = Nextindex (i, Len) of the next entry;                Entry e = tab[i]; Entry is not null,key NULL//Description was originally assigned, but the original threadlocal has been reclaimed if (E! = null && e.get () = = Nu                    ll) {n = len;                    removed = true;                Delete an already invalid entry I = Expungestaleentry (i);            }} while ((n >>>= 1)! = 0);        return removed;            }private int expungestaleentry (int staleslot) {entry[] tab = table;            int len = tab.length;            Recycle Invalid entry Tab[staleslot].value = null;            Tab[staleslot] = null;            size--;            Rehash until we encounter null Entry e;            int i; for (i =Nextindex (Staleslot, Len);                 (e = Tab[i])! = NULL;                i = Nextindex (i, Len)) {threadlocal<?> k = E.get ();                    Entry is not NULL for null,key and should be recycled if (k = = null) {e.value = null;                    Tab[i] = null;                size--; } else {//rehash implementation//Calculate the current entry K's hashcode, see if the subscript should be I//if not for I description,                    Before the hash conflict was put here, now requires Reash int h = k.threadlocalhashcode & (len-1);                        H!=i Description hash Conflict, entry should not be placed under the position of the subscript i if (h! = i) {tab[i] = null;  Unlike Knuth 6.4 algorithm R, we must scan until//NULL because multiple entries could                        have been stale.                       Find the right position H, but it is still possible to conflict so to loop while (tab[h]! = null) H = Nextindex (h, Len); TAB[H] = e;        }}} return i; }

From the above analysis, we can see that the key in the Threadlocalmap is designed as WeakReference, and the set method can be key==null && entry != null judged by whether the entry is invalid .

Summarize the implementation of the Threadlocal set method:

    • Find entry[] array corresponding position setting value according to threadlocal calculation hashcode
    • Iterate through the array to find other invalidated (entry not null,key null) entry Delete
Memory leak issues

Threadlocal minimizes the possibility of memory leaks through clever design, but it is not completely eliminated.

When we do not call the Set/get/remove method After we use the threadlocal, it is possible that the failed memory will not be reclaimed in time, resulting in a memory leak, especially if the value takes up a large memory condition.

So the best practice is to manually call the Remove method to clear the threadlocal when it is no longer in use.

Summarize
    • ThreadLocal does not solve the problem of sharing data between threads
    • Threadlocal is a reference to an object within a thread by allowing the key of the threadlocalmap.entry within the thread to point to itself, making it easy to use variables within threads. It also avoids the problem of instance thread safety because it is all the variables inside the thread.
    • ThreadLocal for scenarios where variables are isolated between threads and shared among methods
    • Threadlocalmap's Entry reference to ThreadLocal is a weak reference, avoiding the problem that the ThreadLocal object cannot be recycled
    • The Threadlocalmap set method reclaims the value of the Entry object with the key null by calling the cleanSomeSlots method (that is, the failed instance) to prevent a memory leak (other remove,get similar)
    • When explicitly threadlocal is no longer in use, manually call the Remove method to empty
Reference

Correct understanding of the principle of thread local and the applicable scenario

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.