ThreadLocal (ThreadLocal, ThreadLocal, ThreadLocal, ThreadLocal, ThreadLocal, ThreadLocal) ¶Copy the code

  • The role of ThreadLocal.

Isolate the private data of multiple threads to prevent the data of one thread from being read and written by other threads in the case of multi-thread concurrency.

ThreadLocal is very simple to use.

ThreadLocal<String> threadLocal = new ThreadLocal<>(); ThreadLocal. Set (threadLocal "test"); String result = threadLocal.get(); System.out.println("result = " + result); //result = test ThreadLocal threadlocal.remove ();Copy the code
  • Set and get and remove methods
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); ThreadLocalMap if (map! = null) map.set(this, value); else createMap(t, value); } /** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for  the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ 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(); /** * post the current thread's value from the yellow post If it has a thread local * variable variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple Invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m ! = null) m.remove(this); }Copy the code
  • The above code is easier to understand, as all three of the above methods use the getMap method
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread  * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; //ThreadLocalMap is a member variable of Thread. Each Thread has its own ThreadLocalMap. Now you can see why ThreadLocal can isolate data between threads, because data is stored in ThreadLocalMap. }Copy the code
  • ThreadLocalMap

It is a map by name, similar to the familiar HashMap, but it has its own characteristics. Let’s take a look at it.

Static class ThreadLocalMap {/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ 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

The structure is roughly as follows:

It is similar to HashMap, but it does not have a linked list or red-black tree interface.

/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ 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) {// The current data location already exists and the key is the same, replace value e.value = value; return; } if (k == null) {// If the current data position is null, create an entry and put it in replaceStaleEntry(key, value, I); return; } // This loop has a hidden condition, if the current location data already exists, but the key is not equal, the next loop "e = TAB [I = nextIndex(I, len)]", continue to find the next location to store. } tab[i] = new Entry(key, value); int sz = ++size; if (! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }Copy the code

E = TAB [I = nextIndex(I, len)];

  • Share data inheritableThreadLocals
//使用方式
private void inheritableThreadLocalTest() {
        inheritableThreadLocal.set("mainThread value");//在mainThread存入值
        new Thread("Thread~1") {
            @Override
            public void run() {
                super.run();
                //Thread~1 的parent是mainThread
                String value = inheritableThreadLocal.get();
                System.out.println("value = " + value + " ,currentThreadName: " + Thread.currentThread().getName());//value = mainThread value ,currentThreadName: Thread~1
            }
        }.start();
    }
Copy the code
例 如 : Public class Thread implements Runnable {/* ThreadLocal values 如 : To this Thread. This map is maintained *  by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; /* * InheritableThreadLocal values pertaining to this thread. This map is * maintained by the InheritableThreadLocal Class. */ / Apart from threadLocals, there is a member variable inheritableThreadLocals, according to the name of speculation is inheritable threadLocals ThreadLocal. ThreadLocalMap inheritableThreadLocals = null; }Copy the code
/** * Initializes a Thread. * * @param g the Thread group * @param target the object whose run() method gets called * @param name the name of the new Thread * @param stackSize the desired stack size for the new thread, or * zero to indicate that this parameter is to be ignored. * @param acc the AccessControlContext to inherit, or * AccessController.getContext() if null * @param inheritThreadLocals if {@code true}, inherit initial values for * inheritable thread-locals from the constructing thread */ private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { ... Int parent = currentThread(); int parent = currentThread(); int parent = currentThread(); It is the thread that created the thread. // How to assign parent's inheritableThreadLocals? The default permissions are not available in our code, so we need to use the JDK to encapsulate InheritableThreadLocal. if (inheritThreadLocals && parent.inheritableThreadLocals ! = null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); }Copy the code
  • Memory leak in ThreadLocal
Static class ThreadLocalMap {//Entry is a weak reference object, ThreadLocal is a weak application object, it will be collected by gc, 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

In ThreadMap, only the key is a weak reference and the value is still a strong reference.

The reference chains are as follows: Thread----ThreadLocalMap----Entry----valueCopy the code

When a ThreadLocal instance is null, there are no strong references to the threadLocal instance, so threadLocal will be collected by gc, but the value will not be collected (there is a strong reference chain) until the Thread is destroyed. But in a thread pool, if threads are not being destroyed that’s not going to work.

Solution: Call the remove method. /** * Remove the entry for key. */ 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 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
  • conclusion

ThreadLocal is relatively uncomplicated, and a few peruses of the source code should make it clear how it works. This is the first time to write an essay. If you find any problems in your reading, please comment and correct them. Code word is not easy, like it. Thank you!