preface

ThreadLocal can be used to pass values in the same thread, but it is not possible to pass values in parent and child threads because the corresponding ThreadLocalMap is not the same thread

The sample

ThreadLocal sample

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(); }}}Copy the code

Results:

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(); }}}Copy the code

Results:

parsing

InheritableThreadLocal inherits a set method that calls a 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);
    }
Copy the code

CreateMap calls its own rewrite

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

We initialize a ThreadLocalMap, but assign to the inheritableThreadLocals and the ThreadLocal to the threadLocals

Moving on to the GET method, which is also the get method that calls 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;
                returnresult; }}return setInitialValue();
    }
Copy the code

The difference is that getMap calls methods that are overridden by InheritableThreadLocal

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

Because at the time of the set is assigned to t.i nheritableThreadLocals, so take the map is from t.i nheritableThreadLocals

ThreadLocal is a ThreadLocal Thread whose parent Thread is a ThreadLocal Thread whose parent Thread is a ThreadLocal Thread whose parent Thread is a ThreadLocal Thread whose parent Thread is a ThreadLocal Thread whose parent Thread is a ThreadLocal Thread. If you look at the Thread constructor, you’ll see that an init method is called

Init method for Thread

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();
        elsethis.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 sizein case the VM cares */
        this.stackSize = stackSize;

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

The above method mainly looks at

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

If Thread(Runnable target, AccessControlContext acc) is false, all constructors are true. If Thread(Runnable target, AccessControlContext ACC) is false, all constructors are true

Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
    }
Copy the code
Thread parent = currentThread(); Here is our main thread, because we use in the main is InheritableThreadLocalTest set (" ye "); So we gave inheritableThreadLocals ThreadLocalMap assignment above all go this. InheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); This is the child thread of the current new

What’s the inheritableThreadLocals assignment for the child thread

 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++; }}}}Copy the code

Take the parent thread’s ThreadLocalMap and copy it (shallow copy, reference copy) so that the inheritableThreadLocals of the thread has the corresponding ThreadLocalMap. This allows ThreadLocalMap to fetch the same value as the parent thread

summary

InheritableThreadLocal inherits ThreadLocal, so uses it the same way as ThreadLocal, The only difference is for the use of ThreadLocal is for the use of ThreadLocal is ThreadLocal ThreadLocalMap threadLocals variables InheritableThreadLocal using ThreadLocal. ThreadLocalMap inheritableThreadLocals variables But are ThreadLocalMap, When we use InheritableThreadLocal, we can use init to copy the parent Thread's ThreadLocalMap when we create a new Thread To the inheritableThreadLocals of the child thread