InheritableThreadLocal源码解析

ThreadLocal在是多线程操作中一个常用的工具类,主要为每个线程提供一个本地副本,避免共享变量的产生。但是他有一个尴尬的情况,就是不支持父子线程之间的传递,但在实际工作中还是会有这样的需求,所以就出现了InheritableThreadLocal,因为之前了解过ThreadLocal的实现,所以对InheritableThreadLocal产生了好奇。

打开InheritableThreadLocal,继承了ThreadLocal,重写了几个方法,主要就是将Thread中的threadLocals替换成了inheritableThreadLocals,在贴张Thread源码的截图应该就清晰了

java 复制代码
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);
    }
}

其余的就跟ThreadLocal流程相同了,这样就很奇怪了,为什么重写了这几个方法就可以进行父子线程的传递,那答案就只能在Thread的实现上,不论是ThreadLocal或者InheritableThreadLocal在我的理解里,他们其实更像工具类,真正的实现线程上的交互还是在Thread进行的。

java 复制代码
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * 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) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
java 复制代码
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        // 获取当前线程,也就是当前Thread启动后的父线程
        Thread parent = currentThread();
        // java线程安全相关
        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();
        //线程名称
        this.name = name.toCharArray();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        // 赋值runnable
        this.target = target;
        setPriority(priority);
        //判断父线程是否构建了InheritableThreadLocals
        if (parent.inheritableThreadLocals != null)
            // 在当前ThreadL对象中构建InheritableThreadLocals对象,将父线程的InheritableThreadLocals对象赋值给当前ThreadL对象
            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在初始化的时候,会判断当前线程是否有inheritableThreadLocals,有的话就会在当前Thread类构建一个相同的inheritableThreadLocals对象,这里有点点绕的就是currentThread()却被称为父线程,因为构建Thread的时候,新的线程还没开启,所以currentThread()获取的线程还是原来的线程。点开createInheritedMap会发现他调用的是ThreadLocalMap方法,会发现有两个构造方法,但是目的是完全不同的。

java 复制代码
         /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;
        //构建新的ThreadLocalMap
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //初始16
            table = new Entry[INITIAL_CAPACITY];
            //hash
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
java 复制代码
      // 根据父线程的ThreadLocalMap构建新的ThreadLocalMap,新的ThreadLocalMap包含父类所有的ThreadLocalMap
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            //获取父类所有的map
            Entry[] parentTable = parentMap.table;
            // 获取父类map的长度
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                // 获取父类map的元素
                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);
                        // 防止hash冲突,找到空的位置插入
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

总的来说InheritableThreadLocal和ThreadLocal的增删改查的逻辑基本相同的,主要还是Thread中多了一个传递的逻辑。

相关推荐
星星不打輰3 分钟前
Spring基于注解进行开发
java·spring
陈大爷(有低保)5 分钟前
Spring中都用到了哪些设计模式
java·后端·spring
程序员 小柴11 分钟前
SpringCloud概述
后端·spring·spring cloud
骑牛小道士11 分钟前
JAVA- 锁机制介绍 进程锁
java·开发语言
高林雨露15 分钟前
Java对比学习Kotlin的详细指南(一)
java·学习·kotlin
喝醉的小喵31 分钟前
分布式环境下的主从数据同步
分布式·后端·mysql·etcd·共识算法·主从复制
雷渊1 小时前
深入分析mybatis中#{}和${}的区别
java·后端·面试
我是福福大王1 小时前
前后端SM2加密交互问题解析与解决方案
前端·后端
亦是远方1 小时前
2025华为软件精英挑战赛2600w思路分享
android·java·华为
花月C1 小时前
Spring IOC:容器管理与依赖注入秘籍
java·开发语言·rpc