This article discusses ThreadLocal in JDK 1.8

ThreadLocal concept

ThreadLocal Provides a copy of a variable for each thread, exchanging space for time.

  • ThreadLocal creates a copy of this variable in each thread, meaning that each thread has an internal copy of this variable, and it can be used anywhere within the thread. Threads are isolated from each other, so that there is no thread-safety problem and no significant impact on program performance
  • Since replicas are created in each thread, consider the cost of resources, such as memory, that would be greater than if ThreadLocal were not used
  • If ThreadLocal is used, it is usually defined as a private static type, but in my opinion it is better to define a private static final type

Usage scenarios of ThreadLocal

I personally think ThreadLocal should be considered as long as the following two requirements are met

  • Each thread needs to have its own separate instance
  • Instances need to be shared among multiple methods, but do not want to be shared by multiple threads

For example: to create A database connection, in the case of multi-threading, we certainly do not want to have A connection before the completion of thread A, thread B closed it or multiple threads sharing A connection resulting in data operation chaos. Our correct posture should result in a code like the following:

private static ThreadLocal<Connection> connTl = new ThreadLocal<>();

public static Connection getConnection(a) throws SQLException{
    Connection conn = connTl.get();
    if(conn==null){
        conn = dataSource.getConnection();
        connTl.set(conn);
    }
    return conn;
}
Copy the code

This section describes common ThreadLocal methods

class ThreadLocal<T> {
    T get(a);
    void set(T value);
    void remove(a);
}
Copy the code

Sets the value of a thread-local variable for the current thread

public void set(T value);
Copy the code

Returns the thread-local variable corresponding to the current thread

public T get(a);
Copy the code

Deletes the value of the thread’s current thread-local variable

public void remove(a)
Copy the code

ThreadLocal source code parsing

Before looking at the source code for common methods, let’s take a look at ThreadLocalMap

ThreadLocalMap is a static class within ThreadLocal

 static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<? >>{ Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}/** * Initial capacity */
        private static final int INITIAL_CAPACITY = 16;

        /** * entity table */
        private Entry[] table;

        /** * Table initial size */
        private int size = 0;

        /** * When size reaches threashold, the entire Map needs to be resized. The initial value of threshold is len * 2/3 */
        private int threshold; // Default to 0

        /** * Sets the resizing threshold to keep the load factor at 2/3 in the worst case. * /
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /** * returns the next index, if the length exceeds 0 */
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /** * returns the last index, or the index */ of length -1 if -1 is negative
        private static int prevIndex(int i, int len) {
            return ((i - 1> =0)? i -1 : len - 1);
        }

        /** * construct argument creates a ThreadLocalMap code * ThreadLocal is key, our generic is value */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);
        }

        [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal] [InheritableThreadLocal
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if(e ! =null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if(key ! =null) {
                        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++; }}}}/** * get the index position of ThreadLocal, get the content by subscript index */
        private Entry getEntry(ThreadLocal
        key) {
            // Use hashcode to determine the subscript
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            // If found, return directly
            if(e ! =null && e.get() == key)
                return e;
            else
                // If you can't find it, start at position I and traverse backwards. Based on the linear detection method, it is possible to find it after I
                return getEntryAfterMiss(key, i, e);
        }
        
        private Entry getEntryAfterMiss(ThreadLocal<? > key,int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            // Loop over backwards
            while(e ! =null) {
                // Get k of the nodeThreadLocal<? > k = e.get();// Return if the value is equal
                if (k == key)
                    return e;
                // If null, a sequential segment clearing is triggered
                if (k == null)
                    expungeStaleEntry(i);
                // get the next index and judge
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

        /** * the set method of ThreadLocalMap, from which we can see that the hash table uses linear detection to resolve conflicts
        private void set(ThreadLocal
        key, Object value) {
            // create a new reference to table
            Entry[] tab = table;
            // Get the table length
            int len = tab.length;
             // Get the subscript of ThreadLocal in the table
            int i = key.threadLocalHashCode & (len-1);

            // Start the loop from the subscript
            for(Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();// If the key is the same, the value is directly replaced
                if (k == key) {
                    e.value = value;
                    return;
                }

                // If the key has been reclaimed, replace the invalid key
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return; }}// Find the empty location, create an Entry object and insert it
            tab[i] = new Entry(key, value);
            // The size of the element in the table increases
            int sz = ++size;
            if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }/** * Remove the key method */
        private void remove(ThreadLocal
        key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return; }}}private void replaceStaleEntry(ThreadLocal<? > key, Object value,int staleSlot) {
            // create a reference variable to table
            Entry[] tab = table;
            // Get the table length
            int len = tab.length;
            Entry e;

            // Record the current failed node subscript
            int slotToExpunge = staleSlot;
            
            /** * scan forward from the staleSlot subscript to find and record the subscript */ whose first position value is null
            for (inti = prevIndex(staleSlot, len); (e = tab[i]) ! =null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;

            /** * Scan backwards from staleSlot subscript */
            for (inti = nextIndex(staleSlot, len); (e = tab[i]) ! =null;
                 i = nextIndex(i, len)) {
                
                // Get the ThreadLocal object corresponding to the Entry nodeThreadLocal<? > k = e.get();/** * If it corresponds to the new key, assign value directly, replace the subscripts */ for I and staleSlot
                if (k == key) {
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;

                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;                    

                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                /* If the current subscript is invalid and no invalid Entry node is found during the backward scan, slotToExpunge is assigned to the current position */
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            // If the key is not found in the table, a new Entry is created in the current position
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);

            if(slotToExpunge ! = staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }/** * set the value of the Entry node from which the ThreadLocal object is collected to null for GC. Rehashes non-NULL entries to the next null position * if the Entry is not at the current position */
        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            // Get the length
            int len = tab.length;

            // Set the passed subscript to null
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;
            
            Entry e;
            int i;
            // Delete all subsequent nodes of the specified node from which ThreadLocal is reclaimed
            for(i = nextIndex(staleSlot, len); (e = tab[i]) ! =null;
                 i = nextIndex(i, len)) {
                // Get the key of the entryThreadLocal<? > k = e.get();// If ThreadLocal is null, set value and array subscript to null for GC and size-1
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {    // If not null
                    // recalculate the subscript of key
                    int h = k.threadLocalHashCode & (len - 1);
                    
                    // If it is the current position, iterate over the next one
                    // if it is not the current position, start from I and find the next coordinate that is null
                    if(h ! = i) { tab[i] =null;
                        
                        while(tab[h] ! =null) h = nextIndex(h, len); tab[h] = e; }}}return i;
        }

        /** * Clears the collected Entry */
        private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
                i = nextIndex(i, len);
                Entry e = tab[i];
                // The Entry object is not empty, but the ThreadLocal key is already null
                if(e ! =null && e.get() == null) {
                    n = len;
                    removed = true;
                    // Call cleanupi = expungeStaleEntry(i); }}while ( (n >>>= 1) != 0);
            return removed;
        }

        /** * rehash */
        private void rehash(a) {
            expungeStaleEntries();

            // Use lower threshold for doubling to avoid hysteresis
            if (size >= threshold - threshold / 4)
                resize();
        }

        /** * expand the size of the table by 2 */
        private void resize(a) {
            Entry[] oldTab = table;
            // Old table length
            int oldLen = oldTab.length;
            // New table length
            int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];
            int count = 0;

            /** * insert into a new table * 1, if the key is null, set the value to null, so that GC can retrieve * 2, calculate the subscript with hashcode & len-1, if there is already an Entry array, Insert */ backwards through linear probe
            for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];
                if(e ! =null) { ThreadLocal<? > k = e.get();if (k == null) {
                        e.value = null; // Help the GC
                    } else {
                        int h = k.threadLocalHashCode & (newLen - 1);
                        while(newTab[h] ! =null) h = nextIndex(h, newLen); newTab[h] = e; count++; }}}// Reset the capacity expansion threshold
            setThreshold(newLen);
            / / update the size
            size = count;
            // Points to a new Entry array
            table = newTab;
        }

        /** * clear all useless entries */ from the table
        private void expungeStaleEntries(a) {
            Entry[] tab = table;
            int len = tab.length;
            for (int j = 0; j < len; j++) {
                Entry e = tab[j];
                if(e ! =null && e.get() == null) expungeStaleEntry(j); }}}Copy the code

The get method

    public T get(a) {
        // Get the current Thread object
        Thread t = Thread.currentThread();
        // Use getMap to obtain the ThreadLocalMap of the Thread
        ThreadLocalMap map = getMap(t);      
        if(map ! =null) {
            // If the map already exists, take the current ThreadLocal as the key to get the Entry object and extract the value from the Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}// If map is empty, setInitialValue is called to initialize it
        return setInitialValue();
    }
Copy the code

GetMap method

    ThreadLocalMap getMap(Thread t) {
        // Return the ThreadLocalMap in the thread
        return t.threadLocals;
    }
    
    // The Java class threadLocals property
    ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code

The ThreadLocalMap reference actually exists in the ThreadLocal class

Entry entity


    //Entry is a key-value structure. The key is ThreadLocal and the value is the stored value
    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

SetInitialValue method

SetInitialValue is called when the Map does not exist

    private T setInitialValue(a) {
        InitialValue (); // call initialValue to generate an initialValue
        T value = initialValue();
        Thread t = Thread.currentThread();
        / / get ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            // createMap is called to create ThreadLocalMap if it does not exist
            createMap(t, value);
        return value;
    }
    
    void createMap(Thread t, T firstValue) {
        // Create a ThreadLocalMap object
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
Copy the code

Set method

    public void set(T value) {
        // Get the current thread
        Thread t = Thread.currentThread();
        / / get the map
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
Copy the code

The map. The set (this, value) method

    private void set(ThreadLocal
        key, Object value) {
    
        Entry[] tab = table;
        int len = tab.length;
        // Calculate the location according to the key
        int i = key.threadLocalHashCode & (len-1);

        // Loop check
        for(Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();// If the Entry already exists and the key is equal to the key passed in, then the Entry is assigned a new value.
            if (k == key) {
                e.value = value;
                return;
            }

            // If an Entry exists but the key is null, replaceStaleEntry is called to replace the Entry with an empty key
            if (k == null) {
                replaceStaleEntry(key, value, i);
                return; }}// Create an entry
        tab[i] = new Entry(key, value);
        / / sz plus 1
        int sz = ++size;
        if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }Copy the code

The remove method

     public void remove(a) {
         / / get the map
         ThreadLocalMap m = getMap(Thread.currentThread());
         if(m ! =null)
             // Call map remove
             m.remove(this);
     }
Copy the code

ThreadLocalMap. Remove (this) method

     private void remove(ThreadLocal
        key) {
         Entry[] tab = table;
         int len = tab.length;
         // Get the index position
         int i = key.threadLocalHashCode & (len-1);
         // loop over the table
         for(Entry e = tab[i]; e ! =null;
              e = tab[i = nextIndex(i, len)]) {
             // If found, call clear correlation method and end the loop
             if (e.get() == key) {
                 // Call WeakRefrence's clear() to clear the reference
                 e.clear();
                 // Clear consecutive segments
                 expungeStaleEntry(i);
                 return; }}}Copy the code

data

  • Java Problem Collection
  • ThreadLocal and ThreadLocalMap source code