博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal学习笔记(二)
阅读量:4210 次
发布时间:2019-05-26

本文共 5675 字,大约阅读时间需要 18 分钟。

上一篇我们基本了解了ThreadLocal的大致过程,也就是Thread的局部变量ThreadLocalMap的相关操作。但是在Thread类中我们看到inheritableThreadLocals变量。而且类型与上期说的ThreadLcoalMap一样。按理说说定义一个ThreadLocalMap就可以,这里为什么要定义两个?

那么我们看看这里这个变量是如何初始化的,一般的类的初始化会在构造方法中进行初始化。我们就看看初始化方法做了哪些工作。

private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {      //这里的inheritThreadLocals表示是否进行同步父子线程的ThreadLocalMap,默认传递true        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);    //判断父线程的inheritableThreadLocals是否为空,不为空的就进行拷贝        if (inheritThreadLocals && parent.inheritableThreadLocals != null)        //将父线程的ThreadLocalMap同步过来            this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);        this.stackSize = stackSize;        tid = nextThreadID();    }  //同步父线程中的变量  private ThreadLocalMap(ThreadLocalMap parentMap) {          //拿到父线程的entity数组            Entry[] parentTable = parentMap.table;            int len = parentTable.length;            setThreshold(len);            table = new Entry[len];            //开始拷贝            for (int j = 0; j < len; j++) {                Entry e = parentTable[j];                if (e != null) {                    @SuppressWarnings("unchecked")          //这里的Key为ThreadLocal类                    ThreadLocal key = (ThreadLocal) e.get();                    if (key != null) {              //调用父类的childValue方法                        Object value = key.childValue(e.value);            //创建一个洗呢Entry元素                        Entry c = new Entry(key, value);                        int h = key.threadLocalHashCode & (len - 1);                        while (table[h] != null)                            h = nextIndex(h, len);                        table[h] = c;                        size++;                    }                }            }    }  //这里看到childValue是需要开发者自己去定义的。  T childValue(T parentValue) {        throw new UnsupportedOperationException();  }

代码看到这里,就比较疑惑了,这里有复制拷贝的操作。但是赋值的操作在哪里??真的是一脸问号。找了半天没有找到,这时候却似需要怀疑一下ThreadLcoalMap定义两个究竟是什么意思。甚至我们之前对ThreadLocal的分析都是有问题的。问题就在于这个inheritableThreadLocals是在何时被赋值的。但是可笑的是想了半天都没有想到。而且这个肯定是和Thread类中的两个ThreadLocalMap挂钩的。

无意中,发现了一个类居然就叫做InheritableThreadLocal!

而且就继承了ThreadLocal,根据java父子类的关系。我们就知道如果子类和父类方法相同,都是走子类的方法。我们看看InheritableThreadLocal都有哪些方法。

在ThreadLocal中

看到这里是不是有种恍然大悟的感觉,所谓的childValue要子类去扩展是啥意思。InheritableThreadLocal就是最明显的子类。也就是说如果我们项目中定义的是InheritableThreadLocal,那么底层的getMap就走的InheritableThreadLocal子类的getMap,也就是返回的是inheritableThreadLocals,也就是说ThreadLocalMap就不用了,所有的数据存储和操作都是inheritableThreadLocals。也就是说使用了InheritableThreadLocal的话,就自然具有父线程局部变量inheritableThreadLocals向子线程局部变量的拷贝。

问题是能不能项目同时使用ThreadLocal和InheritableThreadLocal?我觉得是可以的,因为这两者都有自己的存储容器,而且互相不干扰。而且getMap方法其实在写代码时候就已经决定了究竟走的那个map。所以应该是没有问题的。为了验证上述分析,这里测试一下。看看是否符合预期。

public class User {    private String name;    private String phone;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPhone() {        return phone;    }    public void setPhone(String phone) {        this.phone = phone;    }    @Override    public String toString() {        return "User{" +                "name='" + name + '\'' +                ", phone='" + phone + '\'' +                '}';    }}public class TaskThreadLocalTask implements Runnable{    @Override    public void run() {        //打印threadLocal中的值,按理说是不会被继承的。所以这里的打印为空        User user=MyThreadLocal.get();        String string="";        if (null!=user){            string=user.toString();        }        System.out.println("当前线程:"+Thread.currentThread().getName()+"---信息"+string);        //打印可继承的ThreadLocal的值,这里因为采用的是继承的,所以会打印主进程的参数        System.out.println("当前线程:"+Thread.currentThread().getName()+"---信息"+MyThreadLocal.getInherit().toString());    }}public class TestThreadLocal {    public static void main(String[] args) {        User user=new User();        user.setName("tianjl");        user.setPhone("123123");        System.out.println("主线程"+Thread.currentThread().getName()+"--消息体:"+user.toString());        MyThreadLocal.set(user);        MyThreadLocal.setInherit(user);        ThreadPoolExecutor executor= (ThreadPoolExecutor) Executors.newFixedThreadPool(5);        for (int i=0;i<5;i++){            executor.execute(new TaskThreadLocalTask());        }        //这里的shutdown只是停止线程池添加线程,并不会停止正在运行的线程。        executor.shutdown();    }}

通过上述实验,证明了ThreadLocal是父子线程隔离的,InheritableThreadLocal是可以继承的。而且在项目中两者是可以并存的。

这里测试使用的是线程池,我们知道线程池在没有任务的时候是一直自旋的状态。所以需要在任务执行结束之后关闭线程池。这里调用executor.execute方法执行任务。

转载地址:http://pqkmi.baihongyu.com/

你可能感兴趣的文章
SQL注入漏洞全接触--进阶篇
查看>>
SQL注入漏洞全接触--高级篇
查看>>
SQL注入法攻击一日通
查看>>
论文浅尝 | 通过共享表示和结构化预测进行事件和事件时序关系的联合抽取
查看>>
论文浅尝 | 融合多粒度信息和外部语言知识的中文关系抽取
查看>>
论文浅尝 | GMNN: Graph Markov Neural Networks
查看>>
廖雪峰Python教程 学习笔记3 hello.py
查看>>
从内核看epoll的实现(基于5.9.9)
查看>>
python与正则表达式
查看>>
安装.Net Framework 4.7.2时出现“不受信任提供程序信任的根证书中终止”的解决方法
查看>>
input type=“button“与input type=“submit“的区别
查看>>
解决Github代码下载慢问题!
查看>>
1.idea中Maven创建项目及2.对idea中生命周期的理解3.pom文件夹下groupId、artifactId含义
查看>>
LeetCode-栈|双指针-42. 接雨水
查看>>
stdin,stdout,stderr详解
查看>>
Linux文件和设备编程
查看>>
文件描述符
查看>>
终端驱动程序:几个简单例子
查看>>
登录linux密码验证很慢的解决办法
查看>>
fcntl函数总结
查看>>