the good goal will make you happy and fightful.
During a phone interview with Ali today, I asked ThreadLocal out of the woodwork. Using ThreadLocal is simple
static final ThreadLocal<T> sThreadLocal = new ThreadLocal<T>();
sThreadLocal.set()
sThreadLocal.get()
Copy the code
Threadlocal is an internal thread storage class that can store data in a specified thread. Once the data is stored, only the specified thread can obtain the stored data.
/** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized * copy of the variable. {@code ThreadLocal} instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). */Copy the code
ThreadLocal provides the ability to store variables within a thread. The difference is that the variables read by each thread are independent of each other. The values of the current thread can be obtained using the get and set methods.
To make a bad analogy, ThreadLocal ostensibly maintains a map, with the key being the current thread and the value being the object to store.
ThreadLocal’s static inner class, ThreadLocalMap, maintains an array table for each Thread, and ThreadLocal identifies an array subscript that corresponds to the location of the value store.
As a class that stores data, the key is the GET and set methods.
Public void set(T) {T = thread.currentThread (); ThreadLocalMap map = getMap(t); // Create a map and set if (map! = null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) {ThreadLocalMap return t readlocals; } //createMap void createMap(Thread t, t firstValue) { ThreadLocals. Htreadlocals = new ThreadLocalMap(this, firstValue); }Copy the code
You can see from the above code that each thread holds a ThreadLocalMap object. Each new Thread instantiates a ThreadLocalMap and assigns it to the member variable threadLocals. If threadLocals already exists, the existing object will be used.
Thread
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code
Following the declaration of the ThreadLocalMap part of Thread, look at the instantiation process in the createMap method.
ThreadLocalMap
Set method
//Entry is the ThreadLocalMap static inner class, Static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }} //ThreadLocalMap constructor ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) {// Internal member array, INITIAL_CAPACITY 16 constant table = new Entry[INITIAL_CAPACITY]; / / bit operations, as a result, the same as the modulus, calculate the need to store the location of the / / threadLocalHashCode interesting int I = firstKey. ThreadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }Copy the code
You can see from the above code that an Entry array of length 16 is created when ThreadLocalMap is instantiated. The hashCode and length bits are used to determine an index value I, which is the location stored in the table array.
As described earlier, each Thread holds an instance of ThreadLocalMap, threadLocals, which provides an Entry table for each Thread. And all the reading is done by manipulating the array table.
Table is obviously the focus of set and get, so before we look at the specific set and get methods, let’s look at the following code.
ThreadLocal<A> sThreadLocalA = new ThreadLocal<A>(); ThreadLocal<B> sThreadLocalB = new ThreadLocal<B>(); ThreadLocal<C> sThreadLocalC = new ThreadLocal<C>();Copy the code
We know from the previous section that there is only one ThreadLocalMap for a Thread, so ABC corresponds to the same ThreadLocalMap object. To manage the ABCs, we store them in different locations in an array, which is the Entry array table mentioned above.
How to determine the position of ABC in the table? In order to properly access the corresponding value, there must be a way to calculate the definite index value I, show me code.
// Set method in ThreadLocalMap. private void set(ThreadLocal<? > key, Object value) { // We don't use a fast path as with get() because it is 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(); }Copy the code
Set methods and constructors in ThreadLocalMap can be seen in the following code snippet.
int i = key.threadLocalHashCode & (len-1) int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY – 1) In short, perform a bit operation (modulo) on threadLocalHashCode to get index I. ThreadLocalHashCode looks like this.
Private final int threadLocalHashCode = nextHashCode(); /** * The next hash code to be 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() nextHashCode.getAndAdd(HASH_INCREMENT); }Copy the code
ThreadLocalHashCode = 0x61C88647; static; threadLocalHashCode = 0x61C88647;
0x61C88647 is the Fibonacci hash multiplier. Its advantage is that the results generated by its hash are evenly distributed and can largely avoid hash conflicts. For example, the initial capacity is 16, and the array subscript calculated with the 15-bit hash operation is as follows:
HashCode Array subscript 0x61C88647 7 0xC3910C8E 14 0x255992D5 5 0x8722191c 12 0xe8EA9F63 3 0x4AB325AA 10 0xAC7babf1 1 0xe443238 8 0x700CB87F 15 Summarized as follows:
For a ThreadLocal, its index value I is fixed, and different threads access the same position of different table array, namely table[I], but the table between different threads is independent. For different ThreadLocal instances of the same thread, these ThreadLocal instances share an array of tables, and each ThreadLocal instance has a different index I in the table.
The get () method
Public T get() {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 Entry getEntry(ThreadLocal<? > key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e ! = null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }Copy the code
If you understand the set method, the get method is clear. It simply reads from the corresponding position of the array by calculating the index.
ThreadLocal implementation involves Thread, ThreadLocal, and ThreadLocalMap classes. As described above, the actual code has a lot of details that are not written here.
ThreadLocal features
Both ThreadLocal and Synchronized are designed to solve access conflicts for the same variable in multiple threads
Synchronized solves access conflicts by sacrificing time through thread waiting. ThreadLocal solves conflicts by sacrificing space for each thread. Compared with Synchronized, ThreadLocal has the effect of thread isolation. The desired value cannot be accessed outside the thread. The thread-isolation nature of ThreadLocal makes its application scenario relatively special. ThreadLocal is used in Looper in Android, ActivityThread, and AMS. Consider using ThreadLocal when some data is thread-scoped and different threads have different copies of the data.