InheritableThreadLocal原理解析

Posted cj_eryue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了InheritableThreadLocal原理解析相关的知识,希望对你有一定的参考价值。

在项目上使用ThreadLocal的时候我们一般都会如下这般,这个InheritableThreadLocal 是什么呢?

package com.cjian.threadlocal;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @description:
 * @author: CJ
 * @time: 2021/5/13 14:00
 */
public class ThreadLocalUtils {

    private static ThreadLocal<Map<String, Object>> threadLocalMap;
    
    static {
        threadLocalMap = new InheritableThreadLocal<Map<String, Object>>() {
            protected Map<String, Object> initialValue() {
                return Collections.synchronizedMap(new HashMap());
            }
            protected Map<String, Object> childValue(Map<String, Object> parentValue) {
                return Collections.synchronizedMap(new HashMap(parentValue));
            }
        };
    }


    public static Map<String, Object> getThreadLocals() {
        return (Map) threadLocalMap.get();
    }

    public static Object getThreadLocalByKey(String key) {
        Object value = ((Map) threadLocalMap.get()).get(key);
        return value;
    }

    public static void addThreadLocal(String key, Object value) {
        if (key != null) {
            ((Map) threadLocalMap.get()).put(key, value);
        }
    }

    public static Object removeThreadLocal(String key) {
        return key == null ? null : ((Map) threadLocalMap.get()).remove(key);
    }

    public static void clearThreadLocals() {
        ((Map) threadLocalMap.get()).clear();
    }

  
}

 我们先来看个demo:

package com.cjian.threadlocal;

/**
 * @description:
 * @author: CJ
 * @time: 2021/6/1 10:39
 */
public class inheritableThreadLocalDemo {
    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {

        method1();
        method2();
    }

    private static void method2() {
        // main 线程 set
        inheritableThreadLocal.set(Thread.currentThread().getName()+"2 set value");
        new Thread(()-> {
            // 子线程 t1 get
            System.out.println(Thread.currentThread().getName()+" get value:" +inheritableThreadLocal.get() );
        },"子线程2").start();

        // main 线程get
        System.out.println(Thread.currentThread().getName()+"2 get value:" +inheritableThreadLocal.get() );
    }

    private static void method1() {
        // main线程 set value
        threadLocal.set(Thread.currentThread().getName() + "1 set value");
        new Thread(() -> {
            // 子线程 get
            System.out.println(Thread.currentThread().getName() + " get value:" + threadLocal.get());

        }, "子线程1").start();

        //main 线程 get
        System.out.println(Thread.currentThread().getName() + "1 get value:" + threadLocal.get());
    }

}

运行结果为:

main1 get value:main1 set value
子线程1 get value:null
main2 get value:main2 set value
子线程2 get value:main2 set value

通过代码可以看出,method1中通过main线程往ThreadLocal中设置了一个值,main线程可以成功拿到该值,但是另起的一个子线程1却拿不到;

method2中我们使用InheritableThreadLocal却可以成功拿到,这说明 InheritableThreadLocal 具有继承性。接下来InheritableThreadLocal 是怎么实现的!

package java.lang;
import java.lang.ref.*;

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    
    protected T childValue(T parentValue) {
        return parentValue;
    }
    
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

我们看到InheritableThreadLocal 继承了 ThreadLocal 类。并且重写了父类的 createMap,getMap ,childValue三个方法。
在createMap 和getMap 方法中我们可以看到,将ThreadLocal 方法中的线程threadLocals 属性换成了 inheritableThreadLocals 属性。
我们可以看下Thread类中的这成员定义

到底它是怎么继承父线程属性的呢?这时候我们要从线程的创建开始分析了,看下线程Thread类的构造方法:

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }


private void init(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 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();
        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);
        //通过调用ThreadLocal 的静态方法createInheritedMap 将父线程的 inheritableThreadLocals 属性作为参数去创建 ThreadLocalMap 对象并赋值给自己
        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 */
        tid = nextThreadID();
    }

//参数值为parent.inheritableThreadLocals
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        
        return new ThreadLocalMap(parentMap);
    }
 private ThreadLocalMap(ThreadLocalMap parentMap) {
            //parentMap = parent.inheritableThreadLocals
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            //通过遍历父类的ThreadLocalMap 对象然后赋值到自己的table 中的
            for (int j = 0; j < len; j++) {
                //取出父线程中ThreadLocal里面j下标的Entry 值
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        //调用的是inheritablethreadlocal重写后的childValue方法,可以对继承的值进行加工
                        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++;
                    }
                }
            }
        }

分析完毕~

以上是关于InheritableThreadLocal原理解析的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLoacl,InheritableThreadLocal,原理,以及配合线程池使用的一些坑

InheritableThreadLocal线程复用

每日一博 - ThreadLocal VS InheritableThreadLocal VS TransmittableThreadLocal

InheritableThreadLocal详解

多线程-InheritableThreadLocal

InheritableThreadLocal:子线程继承父线程的本地变量