zoukankan      html  css  js  c++  java
  • InheritableThreadLocal原理解析

    InheritableThreadLocal使用
    在《ThreadLocal内存泄露总结》中ThreadLocal有一个问题就是子线程不能继承父线程的变量。那么本篇所分析的InheritableThreadLocal类就是为了解决这个问题诞生的。
    ThreadLocal问题演示

        public  static  ThreadLocal<String>  threadLocal =new ThreadLocal<>();
        public static void main(String[] args) {
    
            // main线程 set value
            threadLocal.set(Thread.currentThread().getName()+" set value");
            new Thread(()-> {
    
                // 子线程 get
                System.out.println(Thread.currentThread().getName()+" get:" +threadLocal.get() );
    
    
            },"t1").start();
            //main 线程 get
            System.out.println(Thread.currentThread().getName()+" get:" +threadLocal.get() );
        }

    结果:

    main get:main set value
    t1 get:null

    上面演示 ,首先main 线程在ThreadLocal中set 值, 然后又创建子线程 t1获取 ThreadLocal的值,最后main 线程 get 值。
    从执行结果很明显可以看到,子线程t1获取的值是null , main线程 获取到了自己一开始设置的value。所以说ThreadLocal是不支持继承性的。接下来再看下InheritableThreadLocal 的演示

        public  static  InheritableThreadLocal<String> inheritableThreadLocal  = new InheritableThreadLocal<>();
    
        public static void main(String[] args) {
            // main 线程 set
            inheritableThreadLocal.set(Thread.currentThread().getName()+" set value");
            new Thread(()-> {
                // 子线程 t1 get
                System.out.println(Thread.currentThread().getName()+" get:" +inheritableThreadLocal.get() );
            },"t1").start();
            // main 线程get
            System.out.println(Thread.currentThread().getName()+" get:" +inheritableThreadLocal.get() );
        }

    执行结果:

    main get:main set value
    t1 get:main set value

    这里我们还是上面那段代码,但是我们把ThreadLocal换成了InheritableThreadLocal ,从执行结果可以看出来,子线程t1 获得到了父线程main设置的值。说明 InheritableThreadLocal 具有继承性。接下来InheritableThreadLocal 是怎么实现的。
    InheritableThreadLocal 源码分析:

    public class InheritableThreadLocal<T> extends ThreadLocal<T> {
        /**
         * Computes the child's initial value for this inheritable thread-local
         * variable as a function of the parent's value at the time the child
         * thread is created.  This method is called from within the parent
         * thread before the child is started.
         * <p>
         * This method merely returns its input argument, and should be overridden
         * if a different behavior is desired.
         *
         * @param parentValue the parent thread's value
         * @return the child thread's initial value
         */
        protected T childValue(T parentValue) {
            return parentValue;
        }
    
        /**
         * Get the map associated with a ThreadLocal.
         *
         * @param t the current thread
         */
        ThreadLocalMap getMap(Thread t) {
           return t.inheritableThreadLocals;
        }
    
        /**
         * Create the map associated with a ThreadLocal.
         *
         * @param t the current thread
         * @param firstValue value for the initial entry of the table.
         */
        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
        }
    }

    我们看到InheritableThreadLocal 继承了 ThreadLocal 类。并且重写了父类的 createMap,getMap ,childValue三个方法。在createMap 和getMap 方法中我们可以看到,将ThreadLocal 方法中的线程threadLocals 属性换成了 inheritableThreadLocals 属性。我们可以看下Thread类中的这成员定义。

      /* 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;

    可以看到是同类型的不同名属性。其他属性,方法与ThreadLocal是一致的。那么现在肯定有一个问题, 到底它是怎么继承父线程属性的呢?
    继承父线程的属性
    这时候我们要从线程的创建开始分析了。看下线程Thread类的构造方法。

     public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
      }

    我们选了个无参构造,可以看出来构造中又执行了init方法。我们接着往下看。

    private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize, AccessControlContext acc) {
            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 (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();
        }

    我们在注释的①位置处 可以看到 这段代码 Thread parent = currentThread(); 获取了父线程。
    然后我们再往下看,在注释②位置处看到这段代码

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

    通过调用ThreadLocal 的静态方法createInheritedMap 将父线程的 inheritableThreadLocals 属性作为参数创建 ThreadLocalMap 对象赋值给自己。我们看下是怎么创建的。

      static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }

    再看下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 对象然后赋值到自己的table 中的。到这里InheritableThreadLocal 的源码分析就结束了
    总结
    通过上面的演示跟源码分析,我们可以看出InheritableThreadLocal 是ThreadLocal的增强,弥补ThreadLocal不能继承父类元素的缺点。InheritableThreadLocal 继承原理则是通过在创建Thread子线程的时候将父类的inheritableThreadLocals 属性 复制给子线程实现的。

    郭慕荣博客园
  • 相关阅读:
    第一节:从程序集的角度分析System.Web.Caching.Cache ,并完成基本封装。
    大话缓存
    第二节:SQLServer的安装及使用
    OpenCV特征点检测——ORB特征
    Opencv学习笔记--Harris角点检测
    关于Yuri Boykov and Vladimir Kolmogorov 于2004年提出的max flow / min cut的算法的详解
    [论文笔记] CUDA Cuts: Fast Graph Cuts on the GPU
    Graph Cut and Its Application in Computer Vision
    OpenCV中openMP的使用
    四种简单的图像显著性区域特征提取方法-----> AC/HC/LC/FT。
  • 原文地址:https://www.cnblogs.com/jelly12345/p/14969104.html
Copyright © 2011-2022 走看看