InheritableThreadLocal父子线程共享变量

2018-05-31  本文已影响0人  yuan_dongj

前言

学习InheritableThreadLocal之前,需要对ThreadLocal有一定了解,回顾:ThreadLocal 内部实现、应用场景和内存泄漏。ThreadLocalMap是在每个线程之间独有的,不能在其他线程中共享变量。InheritableThreadLocal为解决此问题而生,让我们开始一场学习风暴吧~~~

正文开始

InheritableThreadLocal的源码非常简单,继承自ThreadLocal,重写其中三个方法。

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    public InheritableThreadLocal() {
    }

    protected T childValue(T var1) {
        return var1;
    }

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

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

getMap返回的是inheritableThreadLocals,跟进Thread内的inheritableThreadLocals变量。

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

跟进似乎中断了,InheritableThreadLocal获取的也是ThreadLocalMap ,不过是变量换个名而已,那他是如何做到线程之间共享变量呢?让我们一步步揭开他的神秘面纱~~~

我们仔细想想,InheritableThreadLocal本身并没做什么操作,唯一的可能就是Thread里做了手脚。目前的需求是要求将当前线程里的ThreadLocalMap共享到新开的线程,那么,因为不知道用户何时使用这个数据,所以新开的线程创建好后就必须能访问到这些数据。

没错,你猜对了(终于被我带进坑里了~~)

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        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();
        //..........
        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();
    }

如果当前线程的inheritableThreadLocals != null,新线程:this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)
传入当前线程的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) {
                        //InheritableThreadLocal重新了这个方法 返回原值
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        //计算hash位置,与ThreadLocal的set方法一样
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

千呼万唤始出来啊~~原理很简单,每当新开个线程,创建新的ThreadLocalMap与Entry,遍历原线程Entry[],直接塞到新entry里(浅拷贝),key为原ThreadLocal对象(ThreadLocal需要全局唯一),value为原值。

收尾

上一篇下一篇

猜你喜欢

热点阅读