ThreadLocal的xxxxx

TreadLocal 一般用于防止对全局变量以及单例对象进行共享。TreadLocal 可以提供线程局部变量,每一个线程都会有自己的副本变量,所以get方法总是返回当前执行线程调用的set时设置的最新值。

通过TreadLocal是每一个线程都有属于自己的连接
private static ThreadLocal<Connection> tL = new ThreadLocal<Connection>(){
        @Override
        protected Connection initialValue() {
            Connection connection = null;
            try {
                connection = DriverManager.getConnection("url");
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    };

    public static Connection getConnection() throws SQLException {
        return tL.get();
    }

TreadLocal 会在什么时候给线程赋值?
第一次访问get的时候会给线程赋值,使用重写的initialValue方法分配数据。

TreadLocal 1.8的原理?
ThreadLocal中有一个threadLocals字段,指向堆中的一个ThreadLocalMap,ThreadLocalMap中存储的是当前线程与其他ThreadLocal对象关联的数据。

如何做到线程互不干扰?
线程访问某个ThreadLocal对象的get方法的时候就会检测当前线程的map内是否有key为这个ThreadLocal对象的Entry数据,如果没有的话,这个ThreadLocal的initialValue方法就会创建一个Entry,然后存放到这个ThreadLocalMap里面。

老版本的ThreadLocal是如何设计的?
老版的ThreadLocal(JDK1.7以前)维护在一个大的map里面不利于维护,新版的ThreadLocal,每个线程都会维护自己的数据。当线程被销毁的时候,线程所对应的ThreadLocalMap就会在下一次GC时被回收。ThreadLocalMap中的Entry的key都是弱引用,不参与root算法。

ThreadLocal是作为key存放在ThreadLocalMap中,存放时它使用的Hash是从Object继承下来的hashcode()方法吗?
不是,是重写了,采用黄金分割数来分配hash值的话,映射到散列表就很均匀了(目的:构造冲突较低的散列地址,保证散列表中数据的离散度)。

ThreadLocal为什么要自定义ThreadLocalMap而不是使用JDK所提供的HashMap?
ThreadLocalMap写数据和查数据的过程中,有清理过期数据的功能,将发现的过期数据清理掉,从某方面说也解决了内存泄漏问题(提供了一种策略)。ThreadLocal对应的value如果是过期的一直未被清除,还是会有问题的。
可以把key限定为ThreadLocal这个类型,而且这个Key是个弱引用,而HashMap它使用的key是强引用,弱引用是不影响对象被回收的。

每个线程的ThreadLocalMap对象是什么时候创建的?
延迟初始化,第一次调用Get或者Set方法时,会去检测,如果有就会Get或者Set,如果没有的话会先去创建。
线程的ThreadLocalMap会不会被多次创建?
不会,在线程的生命周期内,ThreadLocalMap仅仅会被初始化一次。

ThreadLocalMap存储的散列表的初始化长度是多少?
16

散列表的长度为什么必须是2的次方数?
二的次方数减一之后是一堆一组成的二进制数,如果数值与这种二进制数进行按位与运算,所得到的数一定是大于等于0且小于等于这个二进制数的,比%(取模算法)效率高很多。因为使用的是位运算。

扩容的阈值是多少呢?它达到阈值就一定会扩容吗?
阈值是当前Entry数组长度的2/3。不会,会rehash一次,全量扫描整个散列表,将过期数据清理掉,当清理掉之后,当前散列表的数据如果仍然达到这个扩容阈值的3/4的话,会进行真正的扩容。

扩容的算法?
首先会创建一个新的数组,长度是原来Entry数组的两倍,然后迭代老的数组,将其中的数据重新按照hash算法放入新的数组里面,迭代完了之后更新ThreadLocalMap对象的这个散列表引用,指向新数组,扩容完成,最后还会计算下次扩容的阈值。

ThreadLocal的get逻辑?
get时候传进来一个ThreadLocal对象,根据这个对象的Hash值按位与当前数组的长度减一,得到一个index,这个散列数组的下标就是这个index元素,可能就是要查找的数据也可能不是,如果不是的话,说明写数据的时候发生过Hash冲突,ThreadLocalMap内部类,Entry是没有next字段的,也就没有办法像HashMap一样冲突之后形成一个链表,ThreadLocalMap采用的策略是hash冲突后,线性向后面找到一个合适的位置去写数据。所以这个Get操作的时候,第一次没有命中的话就会继续向后查找,并且还会检查这个数据是否过期,直到查到这个数据或者遇到null就停止。

第一次get没有get到,向后找的过程中遇到了过期数据,是怎么处理的?
过期数据:ThreadLocalMap内存储的是Entry,Entry有两个字段分别是key value,key是弱引用,指向已经限定的ThreadLocal对象,value是当前线程所保留的变量关联的数据,当key对应的ThreadLocal对象被GC后,由于key是弱引用,所以key的get方法可能会get一个指向null的引用(ThreadLocal被回收后,key.get()null),就代表Entry已经过期了。
处理:触发一次 “探测式” 过期数据回收逻辑,从当前桶位向后迭代,将碰到的key
null的Entry置为null,一直到slot==null为止

探测式清理过期数据,如果在迭代过程中碰到一个正常数据,该怎么办?
如果碰到正常的数据的话,会根据它的key来重新计算出一个Index,看index是否等于当前位置,如果等于,相当于啥也不做,写入时没有发生hash冲突,如果重新计算的index不等于当前位置的话,那说明存数据的时候发生过hash冲突,考虑到当前正在执行清理过期数据的逻辑,当前slot前面可能有过期数据被干掉,所以这个正常数据需要寻找一个更合适的位置去存放,这个位置理论上更接近与正确的index或等于正确的index。

ThreadLocalMap的set方法?
根据Key找到对应下标的slot,如果slot是null,就说明当前set方法是新添数据的逻辑,
如果不是null,第一种情况"添加新数据"逻辑,但是发生hash冲突,会找到一个可以使用的slot插入。
第二种情况,更新,当前过程中查找到key与set的key是一致的,就会替换掉这个Entry中的value
set数据碰到过期数据,做替换数据的逻辑(以当前位置的下一个桶位开始向后查找,直到碰到null或者key一致时它才会停止
遇到key一致,set的数据直接更新到当前的桶位就可以了,让当前的Entry与过期的slot进行一次位置的互换
遍历到null也没有找到key一致的数据,直接就在当前桶位重写一个Entry,相当于抹除了这个过期数据,将新数据放在这里就可以了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值