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

相关推荐
Code apprenticeship几秒前
Java面试题(2)
java·开发语言
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
霖雨3 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
P.H. Infinity4 小时前
【RabbitMQ】07-业务幂等处理
java·rabbitmq·java-rabbitmq