本文已参与「新人创作礼」活动,一起开启掘金创作之路
threadLocal:线程本地对象,专属于这个线程的全局变量,这个线程当中的所有方法都能访问到它
原理:任何一个thread对象,它自己带了一个ThreadLocalMap对象,当我们new出来一个threadlocal并往它里面设置值的时候,他会先去获取这个线程自己的ThreadLocalMap对象,并把threadlocal 自己作为key,设置进去的值作为value
\
内存泄漏:当我们使用线程池获取到一个线程,并且在这个线程当中使用了threadLocal对象,应当及时清理掉,因为线程池中的线程使用完之后不会销毁,而是直接归还到线程池当中,如果这个线程当中的thread local不及时清理,那么上一个使用的threadLocal还会残留在内存当中,导致一直没被清理
应用场景:spring的@transactional注解,当给A()方法加了这个注解以后,表示A()这个方法支持事务,如果A()这个方法面面又调用B()、C()方法,要想着三个方法都在一个事务中,必须保证使用的是同一个数据库连接,spring就是把这个事务连接放在了threadlocal中,当然也可以采用传参的形式,从A中把数据库连接作为参数传递给B、C,只是这种写法更不优雅
弱引用在threadlocal中的体现:当我们给threadlocalset一个变量以后,就会在这个线程的ThreadLocalMap中插入一个键值对,这个map里面世纪存放的是一个个new出来的entry对象(key是threadlocal,value是set的值),而这个entry继承了一个弱引用,entry中的key呢就是通过弱引用的构造方法创建出来的,所以它的key是个弱引用
内存溢出和内存泄漏分别指什么?
内存溢出:内存不够用。
内存泄漏:内存无法释放。
内存泄漏的原因
- ThreadLocalMap内部Entry中key使用的是对ThreadLocal对象的弱引用,这是造成内存泄露是一个原因,因为如果是强引用,那么即使其他地方没有对ThreadLocal对象的引用,但是ThreadLocalMap中key的强引用依然指向ThreadLocal,对象还是不会被回收,而如果是弱引用则这时候ThreadLocal引用是会被回收掉的,但是对于value还是不能被回收,这时候ThreadLocalMap里面就会存在key为null但是value不为null的entry项,虽然ThreadLocalMap提供了set,get,remove方法在一些时机下会对这些Entry项进行清理,但是这是不及时的,也不是每次都会执行的,所以一些情况下还是会发生内存泄露,所以在使用完毕后即使调用remove方法才是解决内存泄露的王道。
也就是说,如果Thread实例还在,但是ThreadLocal实例却不在了,则ThreadLocal实例作为key所关联的value无法被外部访问,却还被强引用着,因此出现了内存泄露。
\
解决办法就是创建ThreadLocal时将ThreadLocal变量设置成全局static类型,当需要存储线程私有的数据时,通过全局的ThreadLocal变量来存,不要在普通方法体中定义局部的ThreadLocal变量来存数据;不要手动将ThreadLocal引用指向null。
threadLocal用途
spring声明式事务:要保证同一个线程中的多个操作都属于同一个事务,那么这些操作就要拿到同一个连接,这个连接就是保存在threadLocal中