zoukankan      html  css  js  c++  java
  • java并发:lnheriitableThreadLocal

    初识lnheriitableThreadLocal

    lnheriitableThreadLocal继承自 ThreadLocal,其提供了一个特性,让子线程可以访问在父线程中设置的本地变量。

    代码示例

      public static void main(String[] args) {
    
        ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(){
          @Override
          protected Integer initialValue() {
            return 0;
          }
        };
    
        threadLocal.set(1);
    
        Thread newThread = new Thread() {
          @Override
          public void run() {
            System.out.println(threadLocal.get());
          }
    
        };
    
        newThread.start();
      }

    上面这段代码,使得newThread访问到了main方法所在线程中的threadLocal变量的变动,即此处newThread将输出1。

    请对比下面这段代码:

      public static void main(String[] args) {
    
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){
          @Override
          protected Integer initialValue() {
            return 0;
          }
        };
    
        threadLocal.set(1);
    
        Thread newThread = new Thread() {
          @Override
          public void run() {
            System.out.println(threadLocal.get());
          }
    
        };
    
        newThread.start();
      }

    此段代码与第一个例子不同的地方在于threadLocal变量的类型不一样,此处newThread将输出0。

    源码分析

    此处将跟踪Thread的创建过程,以搞明白为什么lnheriitableThreadLocal类型的变量可以暴露在新创建的线程中。

        /**
         * Allocates a new {@code Thread} object. This constructor has the same
         * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
         * {@code (null, target, gname)}, where {@code gname} is a newly generated
         * name. Automatically generated names are of the form
         * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
         *
         * @param  target
         *         the object whose {@code run} method is invoked when this thread
         *         is started. If {@code null}, this classes {@code run} method does
         *         nothing.
         */
        public Thread(Runnable target) {
            this(null, target, "Thread-" + nextThreadNum(), 0);
        }
        public Thread(ThreadGroup group, Runnable target, String name,
                      long stackSize) {
            this(group, target, name, stackSize, null, true);
        }
        /**
         * Initializes a Thread.
         *
         * @param g the Thread group
         * @param target the object whose run() method gets called
         * @param name the name of the new Thread
         * @param stackSize the desired stack size for the new thread, or
         *        zero to indicate that this parameter is to be ignored.
         * @param acc the AccessControlContext to inherit, or
         *            AccessController.getContext() if null
         * @param inheritThreadLocals if {@code true}, inherit initial values for
         *            inheritable thread-locals from the constructing thread
         */
        private Thread(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 manager doesn't have a strong opinion
                   on 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(
                            SecurityConstants.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 */
            this.tid = nextThreadID();
        }

    上述代码中着色部分是关键,至此将跳转到ThreadLocal中,代码如下:

        /**
         * Factory method to create map of inherited thread locals.
         * Designed to be called only from Thread constructor.
         *
         * @param  parentMap the map associated with parent thread
         * @return a map containing the parent's inheritable bindings
         */
        static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }

    上述代码调用了ThreadLocalMap的构造函数,代码如下:

            /**
             * Construct a new map including all Inheritable ThreadLocals
             * from given parent map. Called only by createInheritedMap.
             *
             * @param parentMap the map associated with parent thread.
             */
            private ThreadLocalMap(ThreadLocalMap parentMap) {
                Entry[] parentTable = parentMap.table;
                int len = parentTable.length;
                setThreshold(len);
                table = new Entry[len];
    
                for (Entry e : parentTable) {
                    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++;
                        }
                    }
                }
            }

    上述代码中着色部分调用了ThreadLocal中的childValue方法,该方法的代码如下:

        /**
         * Method childValue is visibly defined in subclass
         * InheritableThreadLocal, but is internally defined here for the
         * sake of providing createInheritedMap factory method without
         * needing to subclass the map class in InheritableThreadLocal.
         * This technique is preferable to the alternative of embedding
         * instanceof tests in methods.
         */
        T childValue(T parentValue) {
            throw new UnsupportedOperationException();
        }

    上述方法由lnheriitableThreadLocal重写,与此同时,lnheriitableThreadLocal重写了getMap和createMap方法,整体代码如下:

    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类实例的 set或者 get方法设置变量时,会创建当前线程的 inheritableThreadLocals变量。

    InheritableThreadLocal类通过重写代码,让本地变量保存到了具体线程的 inheritableThreadLocals变量里面。

    当父线程创建子线程时,构造函数会把父线程中 inheritableThreadLocals变量里面的本地变量复制一份保存到子线程的 inheritableThreadLocals 变量里面。 

    思考:

    在什么情况下子线程需要获取父线程的 threadlocal 变量呢,实现方式有哪些? 

    最简单的实现方式:创建线程时传入父线程中的变量,将其复制到子线程中

  • 相关阅读:
    Maven配置阿里云镜像
    docker启动报错 error creating overlay mount
    docker 运行jenkins容器
    centos7 安装docker
    Create PropertyManager Page Example (VB.NET)
    Solidworks api帮助代码报错 “特性说明符不是一个完整的语句。请使用行继续符将该特性应用于下列语句。“的解决办法!
    Solidworks API中在一个拉伸的矩形的前后面建立两个基准面的代码示例
    用vb.net运行一个应用程序,以solidworks为例
    solidworks二次开发中直接录出来的VB代码拿到自己建的项目里出现“未设置对象变量或with块变量”解决办法!
    visual studio xamarin 离线安装文件以及 android 模拟器
  • 原文地址:https://www.cnblogs.com/studyLog-share/p/15117187.html
Copyright © 2011-2022 走看看