Threadlocal Source Code Analysis

Source: Internet
Author: User
Tags rehash

Threadlocal is often used, but has not studied its implementation principle.

Before looking at the source code, I guess it was designed like this: ThreadLocal contains a map<thread,map<threadlocal, V>> property, when used, through Thread.CurrentThread () Gets to the current thread, thus according to Key, finds Map<threadlocal, V>, and later found to be wrong. So this article through reading JDK1.7 source code, to parse threadlocal design thought and principle.


Threadlocal contains only one property Threadlocalhashcode. The comment for this property refers to the Thread.threadlocals, which is a special implementation of the hash table.

public class Threadlocal<t> {/** * threadlocals rely in Per-thread linear-probe hash maps attached * to  Each thread (thread.threadlocals and * inheritablethreadlocals).  The ThreadLocal Objects act as keys, * searched via Threadlocalhashcode. This was a custom hash code * (useful only within threadlocalmaps) that eliminates collisions * in the common case where consecutively constructed threadlocals * is used by the same threads, while remaining well-behaved in * les     s common cases.    */private final int threadlocalhashcode = Nexthashcode (); /** * The next hash code to is given out. Updated atomically.     Starts at * Zero.    */private static Atomicinteger Nexthashcode = new Atomicinteger ();  /** * The difference between successively generated hash codes-turns * Implicit sequential thread-local IDs into     near-optimally spread * Multiplicative hash values for power-of-two-sized tables. */Private Static Final int hash_increment = 0x61c88647;     /** * Returns the next hash code.    */private static int Nexthashcode () {return nexthashcode.getandadd (hash_increment); }......}

And Threadlocal's only attribute Threadlocalhashcode, is used to calculate the Threadlocal<?> key in the Threadlocalmap position, The Getentry () method in Threadlocal.threadlocalmap:

Private Entry Getentry (threadlocal<?> key) {            int i = Key.threadlocalhashcode & (table.length-1); Entry e = table[i];


Thread The class contains a Threadlocal.threadlocalmap attribute, but is maintained (created and used) by the Threadlocal class, designed to allow this threadlocalmap to exist with the thread's life cycle.

  /* ThreadLocal values pertaining to this thread. This map was maintained     * by the ThreadLocal class. */    Threadlocal.threadlocalmap threadlocals = null;


The Getmap () and Createmap () methods in the Threadlocal class:

    /**     * Get The map associated with a ThreadLocal. Overridden in     * inheritablethreadlocal.     *     * @param  t The current thread     * @return the map     *    /Threadlocalmap getmap (thread t) {        return t.t hreadlocals;    }    /**     * Create The map associated with a ThreadLocal. Overridden in     * inheritablethreadlocal.     *     * @param t the current thread     * @param firstvalue value for the initial entry of the map     * @param map the Map to store.     */    void Createmap (Thread T, T firstvalue) {        t.threadlocals = new Threadlocalmap (this, firstvalue);    }



Continue to look at the Get ()/set ()/remove () method in Threadlocal, all in action Threadlocalmap

 /** * Returns The value in the current thread's copy of this * thread-local variable. If the variable have no value for the ' current thread ', it is first initialized to the value returned * by an INVOC     Ation of the {@link #initialValue} method. * * @return The current thread's value of this thread-local */public T get () {Thread T = Thread.curren        TThread ();        Threadlocalmap map = getmap (t);            if (map! = null) {Threadlocalmap.entry E = map.getentry (this);                if (E! = null) {@SuppressWarnings ("unchecked") T result = (t) e.value;            return result;    }} return Setinitialvalue (); }/** * Variant of Set () to establish initialvalue.     Used instead * of set () in case user has overridden the set () method.        * * @return The initial value */private T Setinitialvalue () {T value = InitialValue (); Thread T = Thread.currentthreaD ();        Threadlocalmap map = getmap (t);        if (map! = null) Map.set (this, value);        else Createmap (t, value);    return value;  }/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses'll has a no need to * override this method, and relying solely on the {@link #initialValue} * method     To set the values of Thread-locals.     * * @param value The value of the stored in the current thread's copy of * this thread-local.        */public void Set (T value) {Thread T = Thread.CurrentThread ();        Threadlocalmap map = getmap (t);        if (map! = null) Map.set (this, value);    else Createmap (t, value);  }/** * Removes the current thread ' s value of this thread-local * variable. If this thread-local variable was subsequently * {@linkplain #get read} by the current thread and its value would be * Reinitialized by InvoKing its {@link #initialValue} method, * unless it value is {@linkplain #set set} by the current thread * in the  Interim.     This could result in multiple invocations of the ' {@code InitialValue} method in the current thread.         * * @since 1.5 */public void Remove () {Threadlocalmap m = Getmap (Thread.CurrentThread ());     if (M! = null) M.remove (this); }


The static class Threadlocalmap defined in Threadlocal is actually a special hash table:

/** * Threadlocalmap is a customized hash map suitable only for * maintaining thread local values. No operations is exported * Outside of the ThreadLocal class.  The class is package private to * allow declaration of the fields in class Thread. To help deal with * very large and long-lived usages, the hash table entries use * WeakReferences for keys. However, since reference queues is not * used, stale entries is guaranteed to being removed only when * the table s     Tarts running out of space.         */Static Class Threadlocalmap {/** * The entries in this hash map extend WeakReference, using  * Its main ref field as the key (which are always a * ThreadLocal object).  Note that null keys (i.e. entry.get () * = = NULL) mean that the key is no longer referenced, so the * entry  Can is expunged from table.         Such entries is referred to * as "stale entries" in the code that follows. */Static ClassEntry extends Weakreference<threadlocal<?>> {/** The value associated with this ThreadLocal. */            Object value;                Entry (threadlocal<?> K, Object v) {super (k);            Value = V;         }}/** * The initial capacity--must be a power of.        */private static final int initial_capacity = 16;         /** * The table, resized as necessary.         * Table.length must always be a power of.        */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 ...}


(1) Threadlocalmap resolve hash conflict method and Java.util.HashMap different, here is the conflict, from the index+1 position of the array to start looking for an empty position to put in.


        /**         * Increment i modulo len.         */        private static int nextindex (int i, int len) {            return ((i + 1 < len)? I + 1:0);        }


        /** * Get the entry associated with key. This method * itself handles only the fast Path:a direct hits of existing * key.  It otherwise relays to Getentryaftermiss. This was * designed to maximize performance for direct hits, in part * by making this method readily Inlina         ble.         * * @param key The thread local object * @return the entry associated with key, or null if no such */Private Entry Getentry (threadlocal<?> key) {int i = Key.threadlocalhashcode & (Table.len            GTH-1);            Entry e = table[i];            if (E! = null && e.get () = = key) return e;        else return Getentryaftermiss (key, I, E);         }/** * Version of Getentry method for use when key was not found in * its direct hash slot.    * * @param key The thread local object * @param i the table index for key ' s hash code     * @param e The entry at Table[i] * @return The entry associated with key, or null if no such */            Private Entry Getentryaftermiss (threadlocal<?> key, int i, Entry e) {entry[] tab = table;            int len = tab.length;                while (E! = null) {threadlocal<?> k = E.get ();                if (k = = key) return e;                if (k = = null) expungestaleentry (i);                else i = Nextindex (i, Len);            e = Tab[i];        } return null;         }/** * Set the value associated with key. * * @param key The thread local object * @param value the value to be set */private void S            ET (threadlocal<?> key, Object value) {//We don ' t use a fast path as with get () because it was at Least as common to use Set () to create new entries AS//it is To Replace 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);                 for (Entry e = tab[i];                 E! = null;                E = Tab[i = Nextindex (i, Len)]) {threadlocal<?> k = E.get ();                    if (k = = key) {E.value = value;                Return                    } if (k = = null) {Replacestaleentry (key, value, I);                Return            }} Tab[i] = new Entry (key, value);            int sz = ++size;        if (!cleansomeslots (i, SZ) && sz >= threshold) rehash (); }



(2) The Entry class is designed as a static class Entry extends Weakreference<threadlocal<?>> if there is no strong reference to the ThreadLocal object externally, the JVM The Threadlocal object is recycled when the GC is in.


The Threadlocalmap provides a entry method to clear the failure Expungestaleentry (), by judging Weakreference.get () ==null, to discover the Referent object that was collected by GC entry property , and perform cleanup work:

       /** * Expunge a stale entry by rehashing any possibly colliding entries * lying between Staleslot a  nd the next null slot.  This also expunges * any other stale entries encountered before the trailing null. See * Knuth, Section 6.4 * * @param staleslot Index of slots known to has null key * @retu         RN The index of the next null slot after Staleslot * (all between staleslot and this slot would have been checked         * for expunging).            */private int expungestaleentry (int staleslot) {entry[] tab = table;            int len = tab.length;            Expunge entry at Staleslot 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 ();                    if (k = = null) {e.value = null;                    Tab[i] = null;                size--;                    } else {int h = k.threadlocalhashcode & (len-1);                        if (h! = i) {tab[i] = null; Unlike Knuth 6.4 algorithm R, we must scan until//NULL because multiple entries could have bee                        n Stale.                        while (tab[h]! = null) H = Nextindex (h, Len);                    TAB[H] = e;        }}} return i; }

Finally, attach an online threadlocal class diagram



Also, here are some of the discussion about whether Threadlocal has memory leaks, as described in the following article (the conclusion is that there will be no memory leaks, of course, otherwise the author of the JDK is embarrassed not-_-):

Does threadlocal cause memory overflow?

Threadlocal Memory Leak analysis

Threadlocal Source Code Analysis

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.