美文网首页
InheritableThreadLocal父子线程共享变量

InheritableThreadLocal父子线程共享变量

作者: yuan_dongj | 来源:发表于2018-05-31 15:23 被阅读0次

    前言

    学习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为原值。

    收尾

    • 由于实际使用线程池,线程可能重复使用,使用时需要注意,可能达不到预期的效果。
    • 阿里有个开源项目TransmittableThreadLocal,就是为解决线程池内传递变量(有空再继续学)。

    相关文章

      网友评论

          本文标题:InheritableThreadLocal父子线程共享变量

          本文链接:https://www.haomeiwen.com/subject/ebmqsftx.html