“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
-
Locate elements based on the hash code and array length
-
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.
-
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
-
Get ThreadLocalMap from the current thread, query the Entry of the current ThreadLocalMap variable instance, if not null, get value, return
-
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