ThreadLocal与InheritableThreadLocal

时间:2022-07-23
本文章向大家介绍ThreadLocal与InheritableThreadLocal,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

使用场景

ThreadLocal主要用于在各个线程中保存各自对象的值 , 互不相干.

InheritableThreadLocal可以子线程中访问到父线程中的值.

但是InheritableThreadLocal在子线程创建后 , 则父线程无法再通过setValue将值与子线程共享. 因为 , 父线程中inheritableThreadLocals只会在子线程的构造函数中 , 将该ThreadLocalMap的值复制给子线程.

ThreadLocal原理

  1. 创建ThreadLocal对象后 , 通过set设置当前线程的对象. 如果ThreadLocalMap为空的话 , 则会创建
/**
     * 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);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
  1. Thread创建ThreadLocalMap对象为Thread.threadLocals赋值
    void createMap(Thread t, T firstValue) {
        // 创建ThreadLocalMap对象 , 并且将ThreadLocal对象作为Key , 传入的值作为Value保存
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  1. 创建数组 , 通过hashcode计算出index , 然后将数据保存到对应到数组中
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            // 由于初始数组为16 , 所以用hashcode的低15位作为index , 后续resize后
            // 会使用数组的长度进行计算index
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
  1. 在保存后 , 获取的时候也是通过Thread获取到ThreadLocalMap , 再通过ThreadLocal对象获取到对应的值
    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();
    }
  1. 同时Entry是继承自WeakReference的 , 当没有引用时 , 可能会被GC回收
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

InheritableThreadLocal原理

InheritableThreadLocal继承自ThreadLocal , 也只重写了它里面的几个方法 , 实现的方式也是通过在线程创建的时候 , 会在Child线程构造函数中将Parent线程中的值复制到本线程的ThreadLocalMap中 , 所以在父子线程中访问同一个对象可以得到同一个值.

  1. InheritableThreadLocal重写了以下三个函数 , 所以当使用InheritableThreadLocal保存的数据会保存在t.inheritableThreadLocals
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);
    }
}
  1. 在运行时创建线程时会调用该构造函数
Thread(ThreadGroup group, String name, int priority, boolean daemon) {
        this.group = group;
        this.group.addUnstarted();
        // Must be tolerant of threads without a name.
        if (name == null) {
            name = "Thread-" + nextThreadNum();
        }
        // NOTE: Resist the temptation to call setName() here. This constructor is only called
        // by the runtime to construct peers for threads that have attached via JNI and it's
        // undesirable to clobber their natively set name.
        this.name = name;
        this.priority = priority;
        this.daemon = daemon;
        // 将当前线程传入该函数
        init2(currentThread());
        tid = nextThreadID();
    }

private void init2(Thread parent) {
        this.contextClassLoader = parent.getContextClassLoader();
        this.inheritedAccessControlContext = AccessController.getContext();
        if (parent.inheritableThreadLocals != null) {
            // 由于之前在当前线程保存过inheritableThreadLocals , 所以不会为空
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                    parent.inheritableThreadLocals);
        }
    }
  1. createInheritedMap函数中 , 会将Parent线程中InheritableThreadLocal的值复制到Child线程到inheritableThreadLocals
private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.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++;
                    }
                }
            }
        }
  1. 所以在Child线程中访问inheritableThreadLocals的值 , 就会和Parent线程中访问的值一样了.

例如:

  private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
    private static InheritableThreadLocal<Integer> inheritableThreadLocal =
            new InheritableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {

        integerThreadLocal.set(1001); // father
        inheritableThreadLocal.set(1002); // father

        new Thread(() -> System.out.println(Thread.currentThread().getName() + ":"
                + integerThreadLocal.get() + "/"
                + inheritableThreadLocal.get())).start();

    }

//output:
Thread-0:null/1002