玩转ThreadLocal
ThreadLocal 是啥? 有什么作用?怎么玩? 底层如何实现?
听说过ThreadLocal内存泄漏吗? 为啥?
ThreadLocal key为啥设计成弱引用呢?
三步走玩转ThreadLocal
1.ThreadLocal详解
是啥: ThreadLocal专门为线程服务,为线程提供一个单独的存储数据,对其他线程不可见,目的就是实现线程间资源的隔离。
基本操作:
set() 通过threadLocal向当前线程中存一个线程私有的值 "赵二"
get() 通过threadLocal获取当前线程私有的值
remove() 干掉线程中的当前thraedLocal及值
玩起来:
通过threadLocal,主线程set一个"赵二",AAA线程set一个"张三", BBB线程先set一个"李四" 后set一个"王五"
观察得出结论,
用同一个ThreadLocal 不同的线程set值互不影响,同一个线程set值会覆盖
可以猜测threadLocal这个对象 更像是一个"标识",而不具备存储数据,具体的数据应该是存到了线程当中。
底层实现:
根据表现出来的性质大概也能猜到,每个线程应该会有一个存储threadLocal的地方,根据不同的threadLocal标识能获取到不同数据
答案就在这里,Thread类中的182行,
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap是ThreadLocal的一个静态内部类
这一看 好家伙 这玩意这么像HashMap
其内部又套娃静态内部类Entry,但是这个Entry继承了弱引用这个之后再说
Entry的key就是ThreadLocal,value就是具体的值,ThreadLocalMap内部存的就是一个Entry数组——table
画个图就清楚了,
每个线程Thread中有一个ThreadLocalMap,ThreadLocalMap中存的是Entry数组,key为ThreadLocal,value为具体的值
在看看常用方法就更简单了
获取到当前线程的ThreadLocalMap,把<ThreadLocal,value>存进去
如果当前线程中ThreadLocalMap为null, 那就赶紧创建一个
获取到当前线程的ThreadLocalMap,找到ThreadLocal对应的Entry,返value
2.听说过ThreadLocal内存泄漏吗?
所谓内存泄漏可以这么理解,有的对象明明没有引用了 不需要了,但是内存中干不掉,造成内存浪费
为啥会这样呢,
举个实例,
线程AAA通过threadLocal set"张三"到ThreadLocalMap,
等AAA使用完了threadLocal,不需要他了,但是threadLocal并不会被gc回收掉 包括"张三"这个值,
因为threadLocal此时正作为一个Entry的key,被Entry引用,而Entry又被threadLocalMap引用,
而threadLocalMap属于Thread的全局变量,生命周期与Thread相同,
也就是说Thread不销毁,ThreadLocalMap就不会回收,其中的Entry不会回收,Entry不会回收 造成内存泄漏
(这里假设的threadLocal是普通引用,而实际是弱引用下面再说)
OK,那咋整呢,用完ThreadLocal及时把引用它的Entry干掉呗
threadLocal.remove();
或者干脆点 让线程销毁,一切都over
3.弱引用问题
会经常听到有说内存泄漏是因为ThreadLocal是弱引用造成的,
因为ThreadLocal是弱引用, 弱引用任一次GC都会把他回收,那么Entry的key就为null了,Entry就永远访问不到了,造成内存泄漏
我觉得这样说也对也不对,
内存泄漏主要原因是Entry干不掉,因为他被ThreadLocalMap牢牢地把握住了,只能手动把他remove掉,
弱引用的话只是帮忙把Entry的key干掉。
这么设计的目的就是因为ThreadLocal有一层保护机制,在set()或get()时,如果找出来的key为null,则会帮忙把Entry干掉,帮忙避免一下内存泄漏
so 用完ThreadLocal请及时remove() !