Использование InheritableThreadLocal

Java

предисловие

ранее представленныйThreadLocalИспользование, вы можете передать значение в том же потоке через threadLocal, но вы не можете передать значение в потоке родитель-потомок, потому что это не один и тот же поток, поэтому соответствующий ThreadLocalMap отличается

Пример

Пример ThreadLocal

public class ThreadLocalTest {
    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static String get() {
        return threadLocal.get();
    }

    public static void set(String value) {
        threadLocal.set(value);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            final int j = i;
            ThreadLocalTest.set("ye");
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + ThreadLocalTest.get());
                }
            });

            t.start();
        }
    }
}

результат:

Пример InheritableThreadLocal

public class InheritableThreadLocalTest {
    public static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

    public static String get() {
        return threadLocal.get();
    }

    public static void set(String value) {
        threadLocal.set(value);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            InheritableThreadLocalTest.set("ye");
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + InheritableThreadLocalTest.get());
                }
            });

            t.start();
        }
    }
}

результат:

Разобрать

InheritableThreadLocal是继承ThreadLocal
先看他set方法,set 方法是调用 ThreadLocal的set方法
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

createMap называет свой собственный переписанный

void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

Из вышеизложенного видно, что он похож на createMap ThreadLocal, инициализирующий ThreadLocalMap, но присваивающий его inheritableThreadLocals, а ThreadLocal присваивающий его threadLocals.

Продолжайте смотреть на метод get, метод get также является методом get, который вызывает 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();
    }

Разница в том, что getMap вызывает метод, переопределенный InheritableThreadLocal.

ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

Поскольку при установке он назначается t.inheritableThreadLocals, поэтому при взятии карты он также берется из t.inheritableThreadLocals.

После прочтения методов set и get разница только в том, что доступ к карте разный, но другой разницы я не вижу, просто изменить переменную, чем она отличается от ThreadLocal, как дополнить значение родителя и дочерние потоки передают его Это зависит от инициализации Thread.Посмотрев на конструктор класса Thread, вы увидите, что будет вызван метод init.

Метод инициализации потока

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

Вышеупомянутый метод в основном зависит от

if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

inheritThreadLocals — это входной параметр метода.Из конструктора только Thread(Runnable target, AccessControlContext acc) имеет значение false, а остальные конструкторы — true, поэтому то, что мы передаем здесь, — это true

Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }
Thread parent = currentThread();
这里指的是我们的main线程,因为我们在main里面用的是InheritableThreadLocalTest.set("ye");
所以我们把ThreadLocalMap赋值给了inheritableThreadLocals
综上所述会走this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this 为当前new的子线程                 

Далее нам нужно посмотреть, как назначается inheritableThreadLocals дочернего потока.

 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
    
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++;
                    }
                }
            }
        }    

Приведенный выше код очень прост, то есть получить ThreadLocalMap родительского потока, а затем скопировать (поверхностная копия, эталонная копия), чтобы inheritableThreadLocals дочернего потока имел соответствующий ThreadLocalMap, так что то же значение, что и у родительского поток можно получить через ThreadLocalMap

резюме

InheritableThreadLocal 继承ThreadLocal ,所以用法和ThreadLocal 一样,
唯一不同的是ThreadLocal用的是ThreadLocal 用的是ThreadLocal.ThreadLocalMap threadLocals变量
InheritableThreadLocal用的是ThreadLocal.ThreadLocalMap inheritableThreadLocals变量
但都是ThreadLocalMap,所以get和set本质上是没有区别的
InheritableThreadLocal之所以可以支持父子线程直接的传递
是在new Thread的时候init中  复制父线程的ThreadLocalMap 到子线程的inheritableThreadLocals中