(十六)threadlocal局部变量的底层实现

一、概述

官网解释:ThreadLocal类用来提供线程内部局部变量。这些变量多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量,ThreadLocal实例通常来说都是private static类型。 

1、总结:为每个线程创建一个单独的变量副本,提供保持对象的方法避免参数传递的复杂性不是为了解决多线程访问共享变量

2、应用场景:线程多实例每个线程对应一个实例)的对象的访问,且这个对象很多地方都要用到。

       如:同一网站登录用户,每个用户服务器会为其开一个线程每个线程中创建一个ThreadLocal存用户基本信息,多页面跳转,频繁取用户信息,多线程间没联系且当前线程及时获取想要数据

二、实现原理

ThreadLocal可看做容器,存放着属于当前线程的变量

1、四个对外开放接口方法,设置,访问、删除、初始化

(1) void set(Object value)设置当前线程的线程局部变量的值。 

(2) public Object get()该方法返回当前线程所对应的线程局部变量。 

(3) public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 

(4) protected Object initialValue()返回该线程局部变量的初始值,protected为让子类覆盖而设计。是个延迟调用方法,线程第1次调用get()或set(Object)才执行,仅执行1次,缺省实现直接返回null

2、如何为每个线程维护变量副本?

每个Thread 维护一个 ThreadLocalMap 映射表,key为当前ThreadLocal对象,而value对应线程的变量副本,每个线程可能存在多个ThreadLocal

ThreadLocalMap源码

三、使用场景

生成唯一事务id,并传给manager/DAO类方法,记录日志。解决方案(不好,代码冗余):事务id传给所有方法。

ThreadLocal中设置事务id解决。无论调什么方法,都从threadlocal访问事务id。同时处理多个请求,由于每个请求在框架级别上在单独的线程中处理,因此事务id对于每个线程都惟一,可从线程所有执行路径访问它

四、内存泄漏问题(参考其他博文)

Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value

1)Map针对key用弱引用. threadlocal为null,将被gc回收. 2)但value不能回收,因为存在一条从current thread连过来的强引用. 3)thread结束, current thread不在栈中,强引用才断开, Current Thread, Map, value将全部被GC回收

结论:只要线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,发生内存泄露。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束不会销毁的,会再次使用的。就可能出现内存泄露

泄漏根源:ThreadLocalMap生命周期跟Thread一样长,没手动删除对应key就内存泄漏,不是因为弱引用。

避免泄漏(弱引用作用):每次set和get都检测是否有key为Null在Map中,有就干掉忘记remove,当前线程的其他ThreadLocal用时候也可被干掉。ps:建议开发者自己控制,每次用完ThreadLocal,都调remove清除数据。

四、和hashmap不同

HashMap:数组+链表实现的,

ThreadLocalMap:没有链表,entry弱引用

https://zhuanlan.zhihu.com/p/56214714

https://zhuanlan.zhihu.com/p/158684233

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容