ThreadLocal
Function and Purpose
In multithreading, access to critical sections is usually secured by locking.
However, both optimistic and pessimistic locks can affect performance when concurrency conflicts occur.
ThreadLocal is a “space for time” approach that creates a local variable for each thread so that resources are naturally not contested
use
Public static void main(String[] args) throws InterruptedException {// Lambda expression Supplier initializes ThreadLocal<String> stringThreadLocal = ThreadLocal.withInitial(() -> "init"); ThreadLocal<SimpleDateFormat> formatThreadLocal = new ThreadLocal<>(){@override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); }}; Thread t1 = new Thread(()->{ stringThreadLocal.set("1"); stringThreadLocal.get(); Date now = new Date(); System.out.println(formatThreadLocal.get().format(now)); }); Thread t2 = new Thread(()->{ stringThreadLocal.set("2"); }); T1.setname (" child thread 1"); t1.start(); T2.setname (" child thread 2"); t2.start(); Thread.sleep(1000); }Copy the code
The principle of
Each thread has one ThreadLocalMap object, and a thread can have multiple ThreadLocal objects. These ThreadLocal objects and their values are stored in the ThreadLocalMap.Entry array
Initialize the
You can set the initial value for Entry. Value of each thread by setting the initialization mode
<String> stringThreadLocal = threadlocal. withInitial(() -> "init"); ThreadLocal<SimpleDateFormat> formatThreadLocal = new ThreadLocal<>(){@override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); }};Copy the code
The initial value is set not when the object is created, but when the get method is called and no value is obtained
Get the value
ThreadLocal = thread.currentThread (); ThreadLocal = thread.currentThread (); ThreadLocal = thread.currentThread (); ThreadLocalMap map = getMap(t); ThreadLocalMap = 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(); }Copy the code
Set the value
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
Copy the code
Finally, the value is stored in entry.value and the Entry is used as an element of the Entry array
When searching for a subscript position in the array, hash is used to obtain the subscript position. If the subscript is occupied, the next position is determined until the unoccupied position is found
Memory leak problem
While the key in ThreadLocalMap is a weak reference and is automatically reclaimed when no external strong reference exists, the value in Entry is still a strong reference
The reference chain for this value is as follows:
Since the Entry array is a Thread variable that exists throughout the lifetime of the Thread, the value has a chance to be reclaimed only when the Thread ends and is reclaimed.
When ThreadLocalMap detects that a weak-referenced ThreadLocal has been reclaimed, it calls expungeStaleEntry() to release the value
This method not only releases the value of the ThreadLocal, but continues to traverse the Entry array until the Entry== NULL is retrieved
If the weak reference key of the obtained Entry is NULL, the strong reference value is released and the Entry[I] is set to NULL
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 been stale. while (tab[h] ! = null) h = nextIndex(h, len); tab[h] = e; } } } return i; }Copy the code
ThreadLocal’s set(), get(), and remove() all call this method indirectly
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) // Reclaim key expungeStaleEntry(I); else i = nextIndex(i, len); e = tab[i]; } return null; }Copy the code
As you can see, ThreadLocal is also careful to avoid memory leaks. Not only is the key maintained using weak references, but the key is checked for recycling on each operation, and then the value is reclaimed.
However, ThreadLocal’s approach does not guarantee against memory leaks
In extreme cases, the get() method always accesses a fixed number of existing ThreadLocal’s, the cleanup action never takes place, and there is no chance to call set() and remove(), and the memory leak will still occur.
When you need the ThreadLocal variable, it’s good for the system to call remove()
InheritableThreadLocal
ThreadLocal that can be inherited
The child thread does not get the parent thread’s ThreadLocal object
If we want the child thread to see the parent thread’s ThreadLocal, we can use InheritableThreadLocal. As the name suggests, this is a ThreadLocal that supports parent-child inheritance between threads
However, the following points should be noted:
- The passing of variables occurs when a thread is created. If a thread is used instead of a new thread, it will not work
- The assignment of a variable is copied from the map of the main thread to the child thread, and their value is the same object. If the object itself is not thread-safe, then there is a thread-safe problem