浅谈threadlocal

本文详细介绍了Java中的ThreadLocal,包括其工作原理、应用场景以及可能导致的内存泄漏问题。ThreadLocal为每个线程提供独立的变量副本,确保线程安全。然而,如果不正确使用,可能会引发内存泄漏。解决办法是在使用完毕后及时调用remove方法。此外,ThreadLocal的key设计为弱引用,以防止键被回收后值仍驻留内存。文章还探讨了如何通过线性探测解决哈希冲突。

简单介绍

ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题

应用场景

  • Spring采用Threadlocal的方式,来保证单个线程中的数据库操作使用的是同一个数据库连接,同时,采用这种方式可以使业务层使用事务时不需要感知并管理connection对象
  • 对于一些没有traceid的异步日志,可以利用threadlocal的特性,模拟生成traceid,在需要打日志的地方加上
  • 一个线程横跨多个方法,需要传递对象,类似用户信息,任务信息,会导致过度传参问题,可以用threadlocal改造

工作原理

每个线程维护着一个ThreadLocalMap,每个线程创建ThreadLocal的时候,实际上数据是存在自己线程Thread的threadLocals变量(ThreadLocalMap类型)里面的,在这里插入图片描述
别人没办法拿到,从而实现了隔离。ThreadLocalMap类似与map的数据结构,key 是一个弱引用,也就是 ThreadLocal 本身,而 value 存的是线程变量的值在这里插入图片描述

内存泄漏是怎么回事

ThreadLocal在ThreadLocalMap中是以一个弱引用的身份被Entry中的key引用的。如果ThreadLocal没有被外部强引用时,下次GC时会被回收掉。这是Entry中的key已经被回收了,但是value又是强引用,不会被GC。如果线程一直在运行,value就一直得不到回收
在这里插入图片描述
真实原因:
通过上图,咱们会发现内存泄露都有两个前提:
1、没有手动删除entry
只要在使用完threadlocal,调用其remove方法删除对应的entry,就能避免内存泄露
2、当前线程还在运行
由于threadlocalmap是thread的一个属性,被当前线程所引用,所以他的生命周期和thread一样长,使用完threadlocal后,当前线程也随之结束,threadlocalmap自然也会被gc,从而避免了内存泄露

key要设计为弱引用

首先说说如果设置为强引用会发生什么

如果key设计为强引用,引用Threadlocal的对象被回收,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致内存泄漏

如果设置为弱引用

引用Threadlocal的对象被回收,即使ThreadLocalMap没有手动删除,ThreadLocal也会被回收。value 在下一次 ThreadLocalMap 调用 set、get、remove 的时候会被清除

如何解决hash冲突

采用线性探测方法,存储的时候会用threadlocal对象的hash值,定位到table中的位置i,int i = key.threadLocalHashCode & (len-1)。当前位置为空时,始化一个Entry对象放在位置i上,位置i不为空,如果这个Entry对象的key正好是即将设置的key,那么就刷新Entry中的value,如果位置i的不为空,而且key不等于entry,那就利用固定的算法寻找一定步长的下个位置,直到为空为止

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值