ThreadLocal
每个线程单独存储自己的数据,具有线程隔离的效果
原理:
每一个Thread维护一个ThreadLocalMap
,通过ThreadLocal
这个载体,在使用时变量为空则先创建ThreadLocalMap
并赋值给线程再使用;ThreadLocalMap
内部维护table
数组,所有的数据都存在这里。
1 | static class ThreadLocalMap { |
所以对应的关系如下:
1 | 线程与ThreadLocal:一对多 |
值得注意的是,每次new ThreadLocal
时,都会对散列code自增一个固定值,来使算出的结果更为分布
1 | public class ThreadLocal<T> { |
解决哈希冲突的方式
使用开放地址法解决哈希冲突
哈希冲突解决方式:
- 开放地址法:即假设索引15已经存在值且key不同,则会查看索引16为空则设置进去,否则继续下一个索引
- 链表法:在冲突的地方以链表的形式存储:
HashMap
- 再哈希法:使用不同的哈希函数再次哈希直到不再冲突为止,缺点是增加了计算哈希的时间
- 建立公共溢出区:当发生冲突时,将冲突的数据统一放到溢出区
内存泄漏的原因
如果一个ThreadLocal
不存在外部强引用,则key会被GC回收,这样会导致ThreadLocalMap
中的tables
数组里key为null,而value为强引用;除非线程退出或者调用get()/set()/remove()
等方法触发清除,才会断开对value的强引用。
总结就是因为ThreadLocalMap
的生命周期跟Thread
一样长,没有手动删除数据造成的内存泄漏
解决方式:每次用完ThreadLocal
时都调用remove()
方法清除数据集