InheritableThreadLocal (ThreadLocal的升级,用于将父线程的本地变量传给子线程)

1.子线程如何通过 InheritableThreadLocal 获取父线程的 可继承 线程变量的?

 实现原理:

 

相关类:

/*
 *
 *
 *
 */

package java.lang;
import java.lang.ref.*;
 *
 * @author  Josh Bloch and Doug Lea
 * @see     ThreadLocal
 * @since   1.2
 */

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
  
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *  InheritableThreadLocal  的 set() 方法会 用到 ,来判断  子线程的 
       inheritableThreadLocals (即 map)  是否初始化
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *nheritableThreadLocal  的 set() 方法会 用到 ,来判断  子线程的 
       inheritableThreadLocals (即 map)  是否初始化 ,没有初始化 ,调用 此方法进行初始化 
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
       // 给 子线程 设置  inheritableThreadLocals 属性:在Thread 类里是这样定义的
       //  ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

 

 

 

1.

ThreadLocal 设计初衷是为了在多线程环境下,针对每一个线程能有一个自己的副本,这样可以在一定程度上解决多线程并发修改的问题。但是,我们可以在此基础上做一个拓展,比如context,我们可以利用 ThreadLocal 针对每一个线程都有一个自己的上下文,一般都是写成ThreadLocal<Context>,这样在这个线程上做的所有修改都可以被大家利用到。

先用 ThreadLocal

package com.xxl.job.executor.service.jobhandler;

/**
 * @program: 
 * @description:
 * @author: gyg
 * @create: 2021-08-01 12:46
 **/
public class ThreadLocalTest {

    private static ThreadLocal<Context> context = new ThreadLocal<>();
    static class Context {
        String name;
        int value;
    }


    public static void main(String[] args) {
        Context context = new Context();
        context.name = "mainName";
        context.value = 10;
        // 给父线程 设置 本地变量
        ThreadLocalTest.context.set(context);

        // 创建子线程
        Thread childThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        Context childContext = ThreadLocalTest.context.get();
                        System.out.println(childContext.name);
                        System.out.println(childContext.value);
                    }
                }
        );
        // 启动子线程
        childThread.start();
    }
}
    
    

 

运行 main 方法之后,直接在子线程中抛错,这样确实符合我们的预期,但如果我们想达到子线程可以获取到父线程的 context这样的效果该如何做呢?

首先想到的就是在生成子线程的时候,将父线程 ThreadLocal 里的值传给子线程。这样做虽然能达到效果,但过程比较繁杂,且代码侵入性强。

这个时候就可以用InheritableThreadLocal了。
package com.xxl.job.executor.service.jobhandler;

/**
 * @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal
 * @description:
 * @author: gyg
 * @create: 2021-08-01 12:46
 **/
public class InheritableThreadLocalTest {

    private static InheritableThreadLocal<Context> context = new InheritableThreadLocal<>();
    static class Context {
        String name;
        int value;
    }


    public static void main(String[] args) {
        Context context = new Context();
        context.name = "mainName";
        context.value = 10;
        // 给父线程 设置 本地变量
        InheritableThreadLocalTest.context.set(context);

        // 创建子线程
        Thread childThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        Context childContext = InheritableThreadLocalTest.context.get();
                        System.out.println(childContext.name);
                        System.out.println(childContext.value);
                    }
                }
        );
        // 启动子线程
        childThread.start();
    }
}
    
    

运行结果:

 改造:

package com.xxl.job.executor.service.jobhandler;

import sun.rmi.server.InactiveGroupException;

/**
 * @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal
 * @description:
 * @author: gyg
 * @create: 2021-08-01 12:46
 **/
public class InheritableThreadLocalTest {

    private static InheritableThreadLocal<Context> contextThreadLocal = new InheritableThreadLocal<>();

    private static InheritableThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<>();


    static class Context {
        String name;
        int value;
    }


    public static void main(String[] args) {
        Context context = new Context();
        context.name = "mainName";
        context.value = 10;
        // 给父线程 设置 本地变量
        InheritableThreadLocalTest.contextThreadLocal.set(context);
        InheritableThreadLocalTest.integerThreadLocal.set(10000);

        // 创建子线程
        Thread childThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        Context childContext = InheritableThreadLocalTest.contextThreadLocal.get();
                        System.out.println(childContext.name);
                        System.out.println(childContext.value);
                        Integer sonIntegerValue = InheritableThreadLocalTest.integerThreadLocal.get();
                        System.out.println(sonIntegerValue);
                    }
                }
        );
        // 启动子线程
        childThread.start();
    }
}
    
    

说明子线程 拿到了  父线程的 本地变量: 

源码实现:

① 给子线程 设置 父线程的 线程变量

new Thread 的时候 底层 会走如下方法:

 // 此方法 在 Thead 类里 ,是 在  new Thread 的时候 调用的 

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
      
        // 省略无关代码
         ......
      

        //  获取当前线程(将来的父线程)
        Thread parent = currentThread();
        // 省略无关代码
         ......
        // 将父线程的 值 赋给 子线程 
        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)
       //  设置 子线程 的  inheritableThreadLocals  属性 (是一个map)
            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  属性 

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

如何 创建子线程的 inheritableThreadLocals ?

   
// parentMap 是父线程的 inheritableThreadLocals 
private ThreadLocalMap(ThreadLocalMap parentMap) {
            // 获取父线程 的  map
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            //  创建子线程  的  Map 
            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 : key  是 ThreadLocal 引用 
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        //  while 循环  获取子线程 的空槽位 
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        // 将 刚 构建 子线程  的 Entry  放到 数组 里
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

TheadLocal  里 Entry的 定义:

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

断点测试:

 ② 子线程 读取 从父线程 得来的  线程变量

package com.xxl.job.executor.service.jobhandler;

import sun.rmi.server.InactiveGroupException;

/**
 * @program:  可继承父类变量的 ThreadLocal------>InheritableThreadLocal
 * @description:
 * @author: gyg
 * @create: 2021-08-01 12:46
 **/
public class InheritableThreadLocalTest {



    private static InheritableThreadLocal<Integer> integerThreadLocal = new InheritableThreadLocal<>();


  

    public static void main(String[] args) {
   
        // 给父线程 设置 本地变量
        //InheritableThreadLocalTest.integerThreadLocal.set(10000);

        // 创建子线程
        Thread childThread = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        // 获取 从父线程 得到的值 ,get() 方法 没有重写,还是用父类(ThreadLocal的 )
                        Integer sonIntegerValue = InheritableThreadLocalTest.integerThreadLocal.get();
                        System.out.println(sonIntegerValue);
                    }
                }
        );
        // 启动子线程
        childThread.start();
    }
}
    
    
 public T get() {
        Thread t = Thread.currentThread();
        // 这儿注意 :  InheritableThreadLocal  重写了ThreadLoca 的 getMap 方法,所以
        // 此处 getMap走的是 子类重写后的方法
        //  重写后的方法如下:
        //       ThreadLocalMap getMap(Thread t) {
        //           return t.inheritableThreadLocals;
        //        }   
        //    也就是 说:
         // 从 Thread 对象的 
         // ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;  这个属性里获取值
        // 上面的源码已经分析过,在 new  子线程的时候,会将 父线程的 
        // inheritableThreadLocals  里的值 拷贝给子线程   
        
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值