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中多了一个传递的逻辑。

相关推荐
计算机毕业设计小帅13 分钟前
【2026计算机毕业设计】基于Springboot的校园电动车短租平台
spring boot·后端·课程设计
调试人生的显微镜13 分钟前
Web前端开发工具实战指南 从开发到调试的完整提效方案
后端
静心观复14 分钟前
drawio画java的uml的类图时,class和interface的区别是什么
java·uml·draw.io
Java水解14 分钟前
【SQL】MySQL中空值处理COALESCE函数
后端·mysql
Laplaces Demon15 分钟前
Spring 源码学习(十四)—— HandlerMethodArgumentResolver
java·开发语言·学习
guygg8819 分钟前
Java 无锁方式实现高性能线程
java·开发语言
ss27320 分钟前
手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
java·前端·数据库
Python私教23 分钟前
DRF:Django REST Framework框架介绍
后端·python·django
间彧24 分钟前
Java HashMap如何合理指定初始容量
后端
用户40993225021237 分钟前
PostgreSQL全表扫描慢到崩溃?建索引+改查询+更统计信息三招能破?
后端·ai编程·trae