Typically, variables created in memory can be accessed by multiple threads at the same time, and Java implements thread data isolation through ThreadLocal.

The use of the ThreadLocal

Since we mentioned that ThreadLocal stores thread-isolated variables, we might as well test if this is true. We create two threads and then set the ThreadLocal variable for them.

public class ThreadLocalDemo {
 
 private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
 
 public static void main(String[] args) {
 Thread t1 = new Thread(() -> {  threadLocal.set("this is t1");  try {  TimeUnit.SECONDS.sleep(1);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());  }, "t1");  Thread t2 = new Thread(() -> {  threadLocal.set("this is t2");  try {  TimeUnit.SECONDS.sleep(1);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());  }, "t2");  t1.start();  t2.start();  } } Copy the code

The above code outputs the following two lines showing that ThreadLocal’s variables are indeed thread isolated.

t1: this is t1
t2: this is t2
Copy the code

The principle of ThreadLocal

Before we dive into the ThreadLocal principle, we need to take a quick look at ThreadLocalMap. A threadLocals variables in the Thread class, this is a ThreadLocal ThreadLocalMap types of variables, namely ThreadLocalMap is a static inner class ThreadLocal. A ThreadLocal object is a weak reference object, which means that whenever GC occurs, the ThreadLocal object will be reclaimed.

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

Let’s look at the implementation of the set(T value) method in ThreadLocal.

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map ! =null)
        map.set(this, value);
 else  createMap(t, value); }  ThreadLocalMap getMap(Thread t) {  return t.threadLocals; }  void createMap(Thread t, T firstValue) {  t.threadLocals = new ThreadLocalMap(this, firstValue); } Copy the code

The first step is to get the current thread, get threadLocals based on the current thread, set a value for this variable if it exists, and initialize threadLocals otherwise. The overall idea is sketched through the flow chart.


To summarize, the Thread class maintains a ThreadLocalMap object. A ThreadLocalMap is a static inner class of a ThreadLocal and the Entry key in a ThreadLocalMap is a weak reference to the ThreadLocal object. Value is what you want to store, and it looks something like this.


Let’s look at the method body of get().

public T get(a) {
    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(a) {  T value = initialValue();  Thread t = Thread.currentThread();  ThreadLocalMap map = getMap(t);  if(map ! =null)  map.set(this, value);  else  createMap(t, value);  return value; }  protected T initialValue(a) {  return null; } Copy the code

See if the get() method suddenly opens up? Again, we get the current thread, and then we get the ThreadLocalMap object, and then we get the stored value from the ThreadLocalMap object. Of course, if the ThreadLocalMap object does not exist, then initialize it.

How does ThreadLocal leak memory

Since the key in a ThreadLocalMap is a weak reference to a ThreadLocal, the KEY stored in the ThreadLocalMap will be null if the ThreadLocal is reclaimed by GC. Without manually removing (), the Entry of ThreadLocalMap causes a memory leak for the lifetime of the thread, It is a strong reference relationship of thread ref -> thread -> threadLocals -> entry -> value. So Java actually has some prevention mechanisms for memory leaks. Each call to ThreadLocal’s set(), get(), and remove() methods will reclaim the value of an Entry whose key is empty.

So why are ThreadLocalMap keys designed to be weak references? If the key is a weak reference, at least ThreadLocal will be reclaimed after GC. If the key is a weak reference, at least ThreadLocal will be reclaimed after GC. The next set(), get(), and remove() will also reclaim the value of an Entry whose key is null.

Friends who like this article welcome to pay attention to my public account: “SKY technical Training Guide”