“This is the ninth day of my participation in the August Gwen Challenge.


The ThreadLocal class provides thread-local variables. Unlike normal variables, each thread can access its own independently initialized copy of the variable through its GET or set methods. ThreadLocal instances are private static types that want to associate state with a thread (for example, a user ID or transaction ID). — — — — — – ref; comments

First, take a look at some of the core ThreadLocal source code

Thread t = Thread.currentThread(); 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(); private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); map.set(this, value); createMap(t, value); public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); map.set(this, value); createMap(t, value); public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); m.remove(this); ThreadLocalMap getMap(Thread t) { return t.threadLocals;Copy the code

Several important methods of source code from the ThreadLocal can see that the set, get, remove operation cannot leave the ThreadLocalMap, ThreadLocalMap is the static inner class ThreadLocal, is the core of the ThreadLocal functions, This class is essentially a map, similar to implementations like HashMap, again in key-value form, with an inner class Entry where the key can be thought of as an instance of ThreadLocal, but essentially holds a weak reference to the instance of ThreadLocal.

ThreadLocalMap

ThreadLocalMap is a static inner class of ThreadLocal that hosts the core implementation of ThreadLocal functionality.

Basic properties and Entry inner classes

/ * *

* /

* Entry inherits WeakReference and uses ThreadLocal as the key. If the key is null * (entry.get() == null), the key is no longer referenced and the ThreadLocal object is reclaimed * so the entry can also be removed from the table. static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; Private static final int INITIAL_CAPACITY = 16; private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0; private int threshold; Private void setThreshold(int len) {threshold = len * 2/3; private void setThreshold(int len) {threshold = len * 2/3; }Copy the code

Set method

First, the set method of ThreadLocal is used to understand the storage mechanism of TheadLocalMap.

    public void set(T value) {

        Thread t = Thread.currentThread();

        ThreadLocalMap map = getMap(t);

            map.set(this, value);

            createMap(t, value);
Copy the code

First, we get the current thread, and then we get ThreadLocalMap using the getMap method. What does this getMap do

ThreadLocalMapgetMap(Thread t) {
returnt.threadLocals;
}
Copy the code

ThreadLocalMap is provided by the Thread member property threadLocals, so ThreadLocal does not have a reference to ThreadLocalMap

   ThreadLocal.ThreadLocalMap threadLocals = null;

Copy the code

ThreadLocalMap () : ThreadLocalMap () : ThreadLocalMap () : ThreadLocalMap () : ThreadLocalMap () It is ThreadLocalMap’s set method that sets the values.

private void set(ThreadLocal<? > key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); * If the table[I] on the current index is not empty, * uses nextIndex() to fetch the next (linear probe) without a return. for (Entry e = tab[i]; e ! = null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get(); if (k == key) { e.value = value; return; Table [I]; If (k == null) {replaceStaleEntry(key, value, I); if (k == null) {replaceStaleEntry(key, value, I); return; TAB [I] = new Entry(key, value); TAB [I] = new Entry(key, value); int sz = ++size; * cleanSomeSlots is used to remove LLDB ()==null, * =null && table[index].get()==null * As mentioned earlier, the object associated with the data key has been reclaimed, so the * * * * Entry(table[index]) can be null. * If no entry is cleared and the current usage reaches the size defined by the load factor (2/3 of the length), if (! cleanSomeSlots(i, sz) && sz >= threshold) rehash();Copy the code

First, we need to evaluate the index. How to evaluate the index, we need to look at the constructor of ThreadLocalMap

ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); setThreshold(INITIAL_CAPACITY);Copy the code

FirstKey. ThreadLocalHashCode & (INITIAL_CAPACITY – 1), & (INITIAL_CAPACITY – 1), it is a way of modulus, for the 2 NTH power as modulus modulus, use this to replace % (2 ^ n), That’s why the capacity has to be 2 to the n.

    private final int threadLocalHashCode = nextHashCode();

    private static AtomicInteger nextHashCode =

        new AtomicInteger();

    private static final int HASH_INCREMENT = 0x61c88647;

    private static int nextHashCode() {

        return nextHashCode.getAndAdd(HASH_INCREMENT);
Copy the code

AtomicInteger defines an AtomicInteger that fetches the current value each time HASH_INCREMENT, HASH_INCREMENT = 0x61C88647, related to the Fibonacci sequence. The main purpose of ThreadLocalMap is to evenly distribute hash codes in the 2 ^ N array, i.e. Entry[] table. If hash collisions occur, ThreadLocalMap uses linear elastic to resolve hash collisions. Linear detection of address increments di = 1, 2… , m-1, where, I is the number of detection. This method probes the next address one by one until there is an empty address and then inserts it. If no free address can be found in the entire space, an overflow occurs. Assume that the length of the current table is 16, that is, if the hash value of the calculated key is 14, if the table[14] already has a value and its key is inconsistent with the current key, then a hash conflict will occur. In this case, add 14 and 1 to get 15, and judge by taking table[15]. At this time, if the conflict is still returned to 0, take table[0], and so on, until it can be inserted.

Let’s look at the code for linear detection

       private static int nextIndex(int i, int len) {

            return ((i + 1 < len) ? i + 1 : 0);

        private static int prevIndex(int i, int len) {

            return ((i - 1 >= 0) ? i - 1 : len - 1);
Copy the code

So this set method is basically done, it’s a long one, so let’s summarize it

  1. Locate elements based on the hash code and array length

  2. If key is equal, value is overwritten. If key is null, new key and value are overwritten. At the same time, old data with historical key= NULL is cleared.

  3. If you exceed the threshold, you need to hash again.

The get method

Take a look at the source code for ThreadLocal’s get method

Thread.currentthread (); t = thread.currentThread (); ThreadLocalMap map = getMap(t); if (map ! = null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e ! + + + + = null) {// Return value @suppressWarnings ("unchecked") T result = (T)e.value; return result; return setInitialValue(); Protected T initialValue() {private T setInitialValue() {// get the initialValue, default is null T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); map.set(this, value); CreateMap (t, value); createMap(t, value);Copy the code

For explanation and annotation, look at the getEntry method of ThreadLocalMap

private Entry getEntry(ThreadLocal<? Int I = key.threadLocalHashCode & (table.length-1); Entry e = table[i]; if (e ! = null && e.get() == key) return e; Return getEntryAfterMiss(key, I, e); getEntryAfterMiss(key, I, e); 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) // Clear invalid entry expungeStaleEntry(I); I = nextIndex(I, len); e = tab[i];Copy the code

conclusion

  1. Get ThreadLocalMap from the current thread, query the Entry of the current ThreadLocalMap variable instance, if not null, get value, return

  2. If the map is null, that is, not initialized, go to the initialization method.

The remove method

Similarly, take a look at the source of ThreadLocal’s remove method

public voidremove() { ThreadLocalMap m = getMap(Thread.currentThread()); if(m ! =null) m.remove(this); }Copy the code

You can see that you get the current ThreadLocalMap instance using getMap and then call the remove method in ThreadLocalMap.

private void remove(ThreadLocal<? > key) { Entry[] tab = table; int len = tab.length; Int I = key.threadLocalHashCode & (len-1); // Run the for loop to find the correct key for (Entry e = TAB [I]; e ! = null; E = TAB [I = nextIndex(I, len)]) {if (LLDB () == key) {// call weakRefrence clear() to remove reference e.clear(); // Clear expungeStaleEntry(I); return;Copy the code

Wolong Little Egg: be a restless programmer, pay attention to Wolong little Egg, share the way of advanced technology growth, make progress a little bit every day