ThreadLocal source code analysis and InheritableThreadLocal expansion

ThreadLocal

1 Introduction to ThreadLocal

In many scenarios, we want to pass parameters must be defined by explicit parameters, but when the method stack is deeper, it is particularly inelegant, such as passing user information to the called method:

//controller传递用户信息到serviceA
controller.serviceA(user);

//继续向serviceB传递
serviceA.serviceB(user);

//最终传递到dao层
serviceB.dao(user);
复制代码

Each method needs to define an explicit user parameter, which is very bloated. Is there any other way?

Someone may think of defining a public attribute or static variable, but this will cause a multi-thread shared variable thread unsafe problem, so this public attribute must be locked.

Once locked, the efficiency is not a bit slow, is there a more efficient way? At this time, *** ThreadLocal *** will be used.

The synchronization variables mentioned above adopt unified governance, while the strategy adopted by ThreadLocal is to divide and conquer.

In official terms: The ThreadLocal class is used to provide local variables inside the thread . Such variables can be guaranteed to be relatively independent of variables in other threads when accessed in a multi-threaded environment (accessed via get or set methods) . ThreadLocal instances are generally private statictypes, used to associate threads and thread contexts.

simply put:

  • ThreadLocal provides a copy of the variable inside the thread. This variable is only shared within a single thread, and you can easily access the copy of the ThreadLocal variable within the thread.

  • TreadLocal variable copies between multiple threads do not affect each other.

  • ThreadLocal only survives in the life cycle of the thread and dies as the thread dies (you can also manually call the remove method to remove the ThreadLocal variable).

In this way, the complexity of passing parameters in different methods in a single thread is elegantly solved, because it is a shared variable within each thread, and there is no problem of multi-thread unsafeness.

have to be aware of is:

  • ThreadLocal is not a synchronization mechanism and does not solve the thread safety problem under multithreading.

  • ThreadLocal provides each thread with an independent copy of the variable, thereby isolating concurrent access conflicts of data by multiple threads. Because each thread has its own copy of the variable, there is no problem of unsafe multithreading.

The problem of passing user information mentioned above can be achieved through ThreadLocal:

public final class UserUtil {
	private static ThreadLocal<User> userThreadLocal = new ThreadLocal();
  
 	public static User getUser() {
	    return userThreadLocal.get();
	}
 	public static User setUser(User user) {
	    return userThreadLocal.set(user);
	}
	public static void removeUser() {
			userThreadLocal.remove();
	} 
}
复制代码
//设置User
controller(){
	UserUtil.setUser(user);
}

//获取User
serviceA(){
	UserUtil.getUser();
}
serviceB(){
	UserUtil.getUser();
}
dao(){
  UserUtil.getUser();
}
复制代码

2 The realization principle of ThreadLocal

ThreadLocal creates an independent copy variable for each thread. So how does it work?

From the ThreadLocal source code, you can get the following information:

Get the variables corresponding to ThreadLocal by calling the *** get *** method. The source code of the *** get *** method is as follows:

public T get() {
  	//获取当前线程
    Thread t = Thread.currentThread();
  	//根据线程获取ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
      	//获取Entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            T result = (T)e.value;
            return result;
        }
    }
  	//设置并返回初始值
    return setInitialValue();
}
复制代码

This method first obtains the current thread, and then obtains a *** ThreadLocalMap *** through the current thread. If the ThreadLocalMap is empty, set an initial value and return. If the obtained map is not empty, the corresponding value is obtained according to the current *** ThreadLocal *** and returned.

According to this method we can obtain the following information:

  • The corresponding values ​​of ThreadLocal are stored in *** ThreadLocalMap ***.

  • ThreadLocalMap is obtained based on *** current thread *** instance instance.

  • If *** ThreadLocalMap *** is NULL or ThreadLocal has no corresponding value, return the initial value (setInitialValue method).

Now that we know the value of ThreadLocal exists in *** ThreadLocalMap ***, we continue to look at ThreadLocalMap map = getMap(t);the specific implementation of this code.

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
复制代码

This method returns directly, a *** threadLocals *** attribute in the thread, further tracking can find that this attribute is defined in the *** Thread *** class.

public class Thread implements Runnable {
	...
	
	ThreadLocal.ThreadLocalMap threadLocals = null;
	
	...
}
复制代码

Through the source code, we already know very clearly that *** ThreadLocalMap is saved in Thread *** , and *** ThreadLocal *** corresponding key-value pairs are saved in *** ThreadLocalMap ***. The schematic diagram is as follows:

3 Partial source code analysis of ThreadLocal

In the last section, we have analyzed the source code of the *** get *** method, and then we will analyze the other main source codes.

set method:

public void set(T value) {
  	//获取当前线程
    Thread t = Thread.currentThread();
  	//获取ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
复制代码

The function of the set *** method is to use the current *** ThreadLocal instance *** as the key, and save the corresponding value as a key-value pair into *** ThreadLocalMap ***. If *** ThreadLocalMap *** has not been created then create a new *** ThreadLocalMap .

Create *** ThreadLocalMap ***, the createMap(t, value)specific implementation method is as follows.

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码

The *** createMap *** method directly *** new *** has a *** ThreadLocalMap *** object, the parameters passed in are the current *** ThreadLocal *** instance and a variable value that needs to be saved, Trace into the *** ThreadLocalMap *** construction method.

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
  	//创建Entry数组
    table = new Entry[INITIAL_CAPACITY];
  	//计算元素位置
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  	//创建Entry
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}
复制代码

*** ThreadLocalMap *** construction method uses the initial length *** INITIAL_CAPACITY *** to create an *** Entry *** array, and calculate the index of the initial element.

After saving the *** ThreadLocal key-value pair *** to the corresponding position in the array, set the size to 1, and initialize the lower limit of capacity expansion.

It can be seen from this code that all *** ThrealLocal key-value pairs *** the final storage location is an *** Entry array ***, and the Entry class is defined in the *** ThreadLocalMap *** class.

public class ThreadLocal<T> {    
  ...
    static class ThreadLocalMap {
      ...
        //弱引用
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
      ...
    }
  ...
}
复制代码

Since Entry inherits *** WeakReference ***, ThreadLocal is defined as a weak reference object, so as long as there is no strong reference to ThreadLocal, then ThreadLocal will be recycled regardless of whether the memory is sufficient when triggering GC.

But there is a problem at this time. If the ThreadLocal is recycled, and the value in the corresponding *** Entry *** is not recycled, then this value will never be accessible. As more and more ThreadLocals are recycled, more and more values ​​cannot be recovered, which will eventually cause memory leaks.

Some people would say, is it enough to design strong references? In fact, designing it as a strong reference has no effect. If you do not manually set the reference to null, it will cause the key (ThreadLocal) and value to not be recycled, and eventually cause a memory leak.

Designed as a strong reference will also cause the value to be manually set to null, and the key (ThreadLocal) has not been recycled, it will also cause a memory leak.

This is why ThreadLocal is designed as a weak reference object. When the value is manually recovered, the ThreadLocal is also recycled, which is undoubtedly an insurance.

The above analysis is also often mentioned that after using ThreadLocal, you need to manually call *** set (), get (), remove () *** otherwise a memory leak will occur.

We can see how *** remove () *** handles this problem.

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) {
          	//清除key
            e.clear();
          	//进一步清除
            expungeStaleEntry(i);
            return;
        }
    }
}
复制代码

*** remove () *** loops the table and calls *** e.clear () *** to clear the key.

And the next *** expungeStaleEntry *** method does a few things:

  • Clear the i-th element in *** value *** and *** table *** together.
  • Cycle through the elements in the table to remove the elements with key equal to null
  • Rearrange the elements of the table
private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;
  	//设置value和tab[staleSlot]为null
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;

    Entry e;
    int i;
  	//循环所有元素,并清除key==null的元素
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
      	//清除key==null的元素
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
          	//重新排列元素位置
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                tab[i] = null;
                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    return i;
}
复制代码

Similarly, in the ThreadLocal *** set (), get () *** method will eventually call the *** expungeStaleEntry *** method to clear the value of the key is null, will not repeat them here.

4 Extension of ThreadLocal

ThreadLocal is used to solve the sharing of resources within the thread, and to achieve *** resource isolation between threads ***, but in some scenarios we need to access the resources of the main thread in the child thread, can this be achieved? Of course, this time you need to use another class: InheritabIeThreadLocal .

It is easy to notice in the Thread source code that there is another attribute *** inheritableThreadLocals *** under the *** threadLocals *** attribute.

public class Thread implements Runnable {
	...
	
	ThreadLocal.ThreadLocalMap threadLocals = null;
  
  //继承map
	ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
	...
}
复制代码

The types of these two properties are *** ThreadLocal.ThreadLocalMap ***, and *** inheritableThreadLocals *** is referenced by *** InheritableThreadLocal *** class.

Let's take a look at the source code of *** InheritableThreadLocal *** class

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}
复制代码

InheritableThreadLocal *** inherits *** ThreadLocal and covers the three methods of *** childValue, getMap, and createMap ***.

getMap, createMap *** changed from the original operation *** t.threadLocals *** to the operation *** t.inheritableThreadLocals , and the *** childValue *** method will create inheritance in the *** ThreadLocalMap *** Used in Map.

public class ThreadLocal<T> {
  	...
    //创建一个继承map
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
		...
    //根据parentMap创建一个新的ThreadLocalMap,其中的元素key和value值相同
    private ThreadLocalMap(ThreadLocalMap parentMap) {
        Entry[] parentTable = parentMap.table;
        int len = parentTable.length;
        setThreshold(len);
        table = new Entry[len];
      	//循环创建新的Entry
        for (int j = 0; j < len; j++) {
            Entry e = parentTable[j];
            if (e != null) {
                ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
              	//过滤key等于null的值
                if (key != null) {
                  	//childValue返回的值即是e.value
                    Object value = key.childValue(e.value);
                  	//key,value值保持和父线程一致
                    Entry c = new Entry(key, value);
                    int h = key.threadLocalHashCode & (len - 1);
                    while (table[h] != null)
                        h = nextIndex(h, len);
                    table[h] = c;
                    size++;
                }
            }
        }
    }
  ...
}
复制代码

createInheritedMap *** copies a new *** ThreadLocalMap based on the incoming *** parentMap *** to filter out the value of key equal to null, and other elements key and value values ​​are consistent with the parent thread.

When did the thread create the inheritance map? When the thread is initialized, the *** init *** method of the *** Thread *** class is called, and when we specify *** inheritThreadLocals *** as *** true *** and the parent thread ** * inheritableThreadLocals *** When not equal to null, the thread will create an inherited map.

public class Thread implements Runnable {
  	....
    //绝大部分情况下调用本初始化方法
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
      	//inheritThreadLocals 默认为 true
        init(g, target, name, stackSize, null, true);
    }
  
    //线程初始化
		private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
				...
          //父类线程为创建本线程的运行线程
          Thread parent = currentThread();
      	...
          //创建继承map
       		if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            		this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
      	...
    }
		...
}
复制代码

There are three points here that need your attention:

  • In most cases *** inheritThreadLocals *** defaults to true.

  • The parent Thread parent = currentThread()thread creates a new thread for the running thread , that is, the current running thread creates a new thread. At this time, the running thread is still executing and the new thread has not been initialized, so parent = currentThread().

  • The child thread's *** inheritableThreadLocals *** is copied according to the parent thread's *** inheritableThreadLocals ***, which is equivalent to passing the parent thread *** inheritableThreadLocals *** value, so that the child thread is realized Get the value of the parent thread.

Let's compare the actual use of *** ThreadLocal *** and *** InheritableThreadLocal *** by an example.

package com.gavin.test;

public class ThreadLocalTest {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
      	//设置主线程的值
        ThreadLocalTest.threadLocal.set("threadLocal-main");
      	//启用线程1
        new Thread(() -> {
          	//设置线程1的值
            ThreadLocalTest.threadLocal.set("threadLocal-1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1==" + ThreadLocalTest.threadLocal.get());
        }).start();

      	//启用线程2
        new Thread(() -> {
          	//设置线程2的值
            ThreadLocalTest.threadLocal.set("threadLocal-2");
            System.out.println("线程2==" + ThreadLocalTest.threadLocal.get());
        }).start();

        System.out.println("主线程==" + ThreadLocalTest.threadLocal.get());
    }
}

复制代码

This code defines a *** threadLocal *** attribute, and this attribute is set in all three threads. The output is as follows:

主线程==threadLocal-main
线程2==threadLocal-2
线程1==threadLocal-1
复制代码

It can be concluded that the threadLocal attributes of the three threads do not interfere with each other. Let's change the code and see if the child thread can get the value of the main thread.

package com.gavin.test;

public class ThreadLocalTest {

    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
      
        ThreadLocalTest.threadLocal.set("threadLocal-main");
        new Thread(() -> {
          	//直接打印值
            System.out.println("线程1==" + ThreadLocalTest.threadLocal.get());
        }).start();

        new Thread(() -> {
          	//直接打印值
            System.out.println("线程2==" + ThreadLocalTest.threadLocal.get());
        }).start();

        System.out.println("主线程==" + ThreadLocalTest.threadLocal.get());
    }
}

复制代码

The results are as follows:

线程1==null
主线程==threadLocal-main
线程2==null
复制代码

From the results, it can be concluded that the sub-threads cannot obtain the threadLocal value of the main thread, and once again proves that the threadLocal between threads is isolated from each other.

In order to achieve the child thread access to the value of the main thread, we try to use *** inheritableThreadLocal *** to achieve.

package com.gavin.test;

public class InheritableThreadLocalTest {

    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        InheritableThreadLocalTest.inheritableThreadLocal.set("inheritableThreadLocal-main");
        new Thread(() -> {
            System.out.println("线程1==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
        }).start();

        new Thread(() -> {
            System.out.println("线程2==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
        }).start();

        System.out.println("主线程==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
    }
}

复制代码

The results are as follows:

线程1==inheritableThreadLocal-main
主线程==inheritableThreadLocal-main
线程2==inheritableThreadLocal-main
复制代码

It can be concluded from the results that the sub-thread gets the value of the main thread. If the value of the sub-thread is changed, will it affect the main thread or other sub-threads?

According to the above source code analysis, the main thread will pass its own inheritableThreadLocal *** to the child thread. The child thread re-enters the new Entry object to save the key and value, so the child thread modification will not affect the value of the main thread It will not affect other sub-threads, and will only be passed to its own sub-threads. Let's verify it.

package com.gavin.test;

public class InheritableThreadLocalTest {

    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        InheritableThreadLocalTest.inheritableThreadLocal.set("inheritableThreadLocal-main");
        new Thread(() -> {
            InheritableThreadLocalTest.inheritableThreadLocal.set("inheritableThreadLocal-1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1==" + InheritableThreadLocalTest.inheritableThreadLocal.get());

            new Thread(() -> {
                System.out.println("线程1的子线程==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
            }).start();

        }).start();

        new Thread(() -> {
            InheritableThreadLocalTest.inheritableThreadLocal.set("inheritableThreadLocal-2");
            System.out.println("线程2==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
        }).start();

        System.out.println("主线程==" + InheritableThreadLocalTest.inheritableThreadLocal.get());
    }
}

复制代码

The results are as follows

主线程==inheritableThreadLocal-main
线程2==inheritableThreadLocal-2
线程1==inheritableThreadLocal-1
线程1的子线程==inheritableThreadLocal-1
复制代码

As we guessed:

  • The child thread can get the value of the main thread through *** InheritableThreadLocal *** (in fact, it is a new Entry object).
  • Child thread modification *** InheritableThreadLocal *** value will not affect the value of the main thread and other threads.
  • The child thread's *** InheritableThreadLocal *** will be passed down to its child thread.

Nuggets column: juejin.im/user/5ba21d ...

Author: GavinKing

Originality is not easy, please obtain the author's consent for reprinting, and bring copyright information, thank you

Guess you like

Origin juejin.im/post/5e9d5a4e6fb9a03c8027b142