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 变量呢,实现方式有哪些? 

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

posted @ 2021-08-09 09:18  时空穿越者  阅读(84)  评论(0编辑  收藏  举报