• InheritableThreadLocal源码阅读


    说明

    继前面《ThreadLocal源码阅读》知道ThreadLocal原理是获取当前线程Thread的一个Map,通过Entry封装 key为ThreadLocal对象自身 value为我们的值。但是ThreadLocal本身不是子线程共享的。InheritableThreadLocal就是为了解决子线程共享问题

    如何实现子线程共享

    我们知道线程变量都是通过Thread对象的threadLocals存储,其实除了这个线程对象还提供子线程共享的Map inheritableThreadLocals,创建子线程此变量会默认复制一份父线程此Map,注:是复制并不是继承 所以还是线程安全的,子线程set值并不会影响父线程或者其他子线程的值,默认是浅克隆,如果是操作ThreadLocal保存的引用对象则会实现修改,我们可以在childvalue自定义实现深克隆

     /*  当前线程共享 子线程并不能共享
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
        /*
         * 
         * 创建线程时子线程会复制一份
         */
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    继承关系

    重写的三个核心方法

        /**
         * 复制父类ThreadLocalMap的时候调用 原路复制不做任何处理,比如浅克隆或者深克隆或者对复制的值做处理
         * @param parentValue
         * @return
         */
        protected T childValue(T parentValue) {
            return parentValue;
        }
    
        /**
         * 重写getMap获取线程对象的inheritableThreadLocals 后续复制也是复制此对象 具体可以看ThreadLocal源码
         *
         * @param t the current thread
         */
        ThreadLocal.ThreadLocalMap getMap(Thread t) {
            return t.inheritableThreadLocals;
        }
    
        /**
         *
         * @param t the current thread
         * @param firstValue value for the initial entry of the table.
         */
        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
        }

    如何实现复制

    <1>

     //<2>构造函数实现复制
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
    
                }
            });

    <2>

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

    <3>

    private void init(ThreadGroup g, Runnable target, String name,
            long stackSize) {
                //<4>最后一个参数为true表示复制父线程 
                init(g, target, name, stackSize, null, true);
            }

    <4>

       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) {
              
                if (security != null) {
                    g = security.getThreadGroup();
                }
    
           
                if (g == null) {
                    g = parent.getThreadGroup();
                }
            }
    
          
            g.checkAccess();
    
          
            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);
            //inheritThreadLocals为true 同时inheritableThreadLocals不为空则复制父线程的
            if (inheritThreadLocals && parent.inheritableThreadLocals != null)
                //<5>复制父线程的inheritableThreadLocals
                this.inheritableThreadLocals =
                        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
            /* Stash the specified stack size in case the VM cares */
            this.stackSize = stackSize;
    
            /* Set thread ID */
            tid = nextThreadID();
        }

    <5>

     static ThreadLocal.ThreadLocalMap createInheritedMap(ThreadLocal.ThreadLocalMap parentMap) {
            //<6>复制
            return new ThreadLocal.ThreadLocalMap(parentMap);
        }

    <6>

     private ThreadLocalMap(ThreadLocal.ThreadLocalMap parentMap) {
            ThreadLocal.ThreadLocalMap.Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new ThreadLocal.ThreadLocalMap.Entry[len];
            //遍历inheritableThreadLocals的值
            for (int j = 0; j < len; j++) {
                ThreadLocal.ThreadLocalMap.Entry e = parentTable[j];
                if (e != null) {
                    //这里获取到的是InheritableThreadLocal
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        //调用childValue 被重写,默认是原值返回 表示是浅克隆
                        Object value = key.childValue(e.value);
                        //用新的Entry封装保存到当前map
                        ThreadLocal.ThreadLocalMap.Entry c = new ThreadLocal.ThreadLocalMap.Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

    浅克隆的体现

    package other.sort;
    
    import java.io.IOException;
    
    /**
     * @author liqiang
     * @date 2022/2/23
     */
    public class psvm {
    
         String name;
         String age;
    
        public psvm(String name, String age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "psvm{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    
        public static void main(String[] args) throws Exception {
            ThreadLocal<psvm> threadLocal = new InheritableThreadLocal();
            psvm zs = new psvm("zs", "12");
            threadLocal.set(zs);
            new Thread(()->{
                psvm psvm = threadLocal.get();
                System.out.println(threadLocal.get());
                psvm.age= "13";
            }).start();
            Thread.sleep(3000);
            System.out.println("main"+threadLocal.get());
            System.in.read();
        }
    }

  • 相关阅读:
    WEB环境安装步骤v1.2
    将m3u8格式转成mp4格式
    MySQL简介及安装v0.1
    使用脚本pull阿里云的k8s镜像并更改标签
    常用脚本
    常用命令
    记录一下环境变量IFS特定场景使用技巧
    hp-unix创建和更改LV
    HP-UNIX常用命令
    Linux集群搭建
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15926150.html
Copyright © 2020-2023  润新知