ThreadLocal总结问题

2022-02-16 10:04:02 浏览数 (2)

前期两篇ThreadLocal相关文章,我们大概了解其运行原理。分别是ThreadLocal浅析深入细节ThreadLocalMap带着问题去学习,加深理解。

相关问题

1.为什么Entry key是弱引用,而value是强引用?

个人理解就是在保证value可使用的情况下,降低内存占用。

我们从源码中可以看出,虽然Entry中key,也就是ThreadLocal,是弱引用,但同时ThreadLocal也是被外部引用的,ThreadLocal既是Entry中的key,也是外部对象实例。

key是弱引用,他的价值体现只有在ThreadLocal外部对象引用计数为0的情况才能体现出来。举一个场景,一个线程中,多个ThreadLocal对象,在他们引用计数不为0的情况下,Entry中的key,也就是ThreadLocal,gc的情况下,是不会被回收的。只有你不想用其中某些ThreadLocal实例了,把他们设为null,这个时候gc,可以回收key。这个不难理解吧。

那为啥value不是弱引用?个人理解,可能你在设置某些ThreadLocal=null时,在后面继续使用该key对应的value,如果value也是弱引用,那也会被回收,数据会出问题。

有人会想,key被回收后,后面get取不到数据,导致数据丢失。我理解是,你都把ThreadLocal设为null了,你还用他?

2.ThreadLocal内存泄漏问题

通过 深入细节ThreadLocalMap 这篇文章,我们知道在set、get方法中都会清理ThreadLocalMap数组,所以正常情况下,不会存在内存泄漏问题。

但是在ThreadLocal在没有外部对象强引用时,发生GC会导致弱引用Key会被回收,后面又没有set、get操作,导致Value不会回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。所以当我们完成任务后,最好手动调用下remove方法。

3.ThreadLocalMap和HashMap有什么区别?

HashMap相关知识可以看下 HashMap之扩容 这篇文章文章。

1.结构不同

HashMap 的数据结构是数组 链表,ThreadLocalMap的数据结构仅仅是数组。

2.解决hash冲突方式不同

解决hash冲突,通常是两种方法:链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位

HashMap解决hash冲突用的是链表法

新添加的 Entry 对象放入 table 数组的 bucketIndex 索引处。

如果 bucketIndex 索引处已经有了一个 Entry 对象,那新添加的 Entry 对象指向原有的 Entry 对象(产生一个 Entry 链)。

如果 bucketIndex 索引处没有 Entry 对象,也就是上面程序代码的 e 变量是 null,也就是新放入的 Entry 对象指向 null,也就是没有产生 Entry 链。

ThreadLocalMap解决hash冲突用的是开放地址法

和HashMap的最大的不同在于,ThreadLocalMap结构非常简单,没有用链表的方式,而是采用线性探测的方式,就是根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。

ThreadLocalMap使用开放地址法原因

ThreadLocal 往往存放的数据量不会特别大,冲突概率低,单纯数组结构更省空间,同时数组的查询效率也是非常高。

还有其他关于ThreadLocal的问题,后面再继续加。

0 人点赞