java线程(二)ThreadLocal

ThreadLocal

存取元素实际上是以当前threadlocal为key存储的,值为value存储到线程对象thread中的threadlocalmap中。

添加元素执行结束后要remove,否则如果与线程池配合使用,会导致元素一直存在线程中被占用,如果没有再次被使用既会变成内存泄漏,如果再次使用可能会有脏数据的问题。(为什么强调线程池,因为在线程池中线程才不会回收,线程如果被回收自然不会存在内存泄露)

允许设置默认值,如果当前值为空,会通过调用获取初始化值。

threadlocalmap

thread类的内部类,没有实现map接口,底层也是使用entry存储key value,通过entry数组存储多个key value。但是key固定只能是threadlocal类型,解决hash冲突的方式不是通过链地址法,而是通过开放地址法,通过位置加1的方式查找空位置,这种方式会导致冲突的几率高,降低效率,因为不适合存储过多键值对。get方法在计算hash到对应位置,发现key对不上会往后+1遍历,直到找到的位置是空的才停。需要注意的是往后找的过程如果有entry不为空,key为空的情况,会主动清除value释放,并将entry置空。避免内存泄露的手段之一

Threadlocalmap对threadlocal是弱引用,因此如果threadlocal没有被外部其他对象强引用时,在下一次垃圾回收后则会被回收。而对value是强引用的,不remove会导致内存泄漏。(不过get方法查到这个泄漏的位置会主动清除)

三者的关系

Thread -> ThreadLocalMap -> ThreadLocal(map的key)。假设线程不会被回收,ThreadLocal不被外部强引用后会被回收,value是被ThreadLocalMap强引用的,如果线程不被回收则ThreadLocalMap不会回收,value没有清除,则value会占用内存,而如果ThreadLocal已经没用了被回收了,那么这时这个value已经拿不到了,但是确占用内存,造成内存泄露。

为什么value不设置弱引用,因为value不一定要被外部引用的,只要通过ThreadLocal获取到就行,所以ThreadLocalMap必须要保证强引用。而ThreadLocal如果没有被引用则说明没用了,所以设置为弱引用,随时清除。

应用场景

1. 比如通过springmvc进行请求拦截,从redis中取出用户信息进行权限校验,可以把用户信息存储到ThreadLocal中,避免多次操作redis。

2. 比如责任链模式,调用一系列实现业务处理后,可以把业务处理结果封装到ThreadLocal,在上层通过ThreadLocal取到所有处理结果后对结果进行统一处理。

3. 框架里面比较常用,比如spring的事务处理,就是把连接暂存到ThreadLocal,mybatis或者重入事务就能保证拿到同一个连接,保证在一个事务中。

容易误解的点

通过ThreadLocal获取的对象赋给一个引用,根据这个引用对这个对象的改动,ThreadLocal重新获取也会被改动。因为获取到的是value,改动和读取是指向同个对象

通过ThreadLocal获取的对象赋给一个引用,remove后这个引用对应的对象还在。因为remove只是把这个对象和ThreadLocalMap的Entry的value引用关系解除(置空entry,key,value),这个对象已经赋给外部引用后,不会回收。

 

InheritableThreadLocal

Thread类除了包含key为ThreadLocal的ThreadLocalMap,还包含了key为 ThreadLocal的子类InheritableThreadLocal的 ThreadLocalMap(InheritableThreadLocal做key)。

作用:用于将父线程存储的值传给子线程使用(线程a创建线程b,线程b就是线程a的子线程,Thread会把当前创建线程的线程当作父线程)。当前线程创建子线程的时候,Thread的init方法会把当前线程的InheritableThreadLocal的ThreadLocalMap遍历设置到子线程的InheritableThreadLocal的ThreadLocalMap 。

如果InheritableThreadLocal存放的是map对象的话,那么传递给子线程也会指向同个对象,因为是引用传递,不是深拷贝。那么两个线程修改的值都会反应到对方上。如果传递的是基本数据类型,那么传递的值只有创建子线程可以设置进去,之后父线程再修改这个InheritableThreadLocal也不会影响子线程。

可以用与父线程和子线程的通信,也可以依赖浅拷贝,让子线程把处理结果交给父线程。

你可能感兴趣的:(Java基础)