ThreadLocal线程局部变量

143 阅读3分钟
本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1. 为什么要用ThreadLocal?

  • 在很多情况下,多个线程同时修改公共变量,可能会造成异常,会对最终的结果产生影响。
  • 而在高并发的场景下,会造成大量的锁阻塞,影响项目的响应时间。
  • ThreadLocal的思路是:空间换时间,在每个线程中都有一个副本,改变副本中的数据,不会对其他的线程造成影响。

2. ThreadLocal的原理是什么?

  • ThreadLocal中包含一个静态内部类,叫ThreadLocalMap
  • ThreadLocalMap里面包含一个静态的内部类Entry,该类继承于WeakReference (弱引用)类,说明Entry是一个弱引用。
  • ThreadLocalMap内部还包含了一个Entry数组,其中:Entry = ThreadLocal + value。
  • ThreadLocalMap被定义成了Thread类的成员变量。

截图.png

截图.png

3. 为什么用ThreadLocal做key?

  • 如果只定义了一个ThreadLocal对象,那么可以用Thread做key。但如果定义了多个ThreadLocal对象,那么就不能准确的获取自己想要的数据。但如果使用ThreadLocal做key,就可以使用ThreadLocal中的get方法,获取到数据

4. Entry的key为什么设计成弱引用?

  • 为防止内存泄露。
  • 如果key设置成强引用,那么GC就不会对其回收。这时使用了线程池,就会导致强引用链的产生。原因:
  1. ThreadLocal变量对ThreadLocal对象是强引用。ThreadLocal变量生命周期结束,但是key还是引用ThreadLocal对象,如果执行该代码的线程使用了线程池,这样就会导致强引用链的产生。但如果key是弱引用的话,GC做回收的时候,就会将其回收掉,并把值设为nul
  • ThreadLocal变量已经指向null,即生命周期结束,这样调用get,set,remove方法会产生空指针异常,但如果系统中还定义了另外一个ThreadLocal变量b,调用了它的get、set或remove,三个方法中的任何一个方法,都会自动触发清理机制,将key为null的value值清空。
  • 如果key和value都是null,那么Entry对象会被GC回收。如果所有的Entry对象都被回收了,ThreadLocalMap也会被回收了。

这样会最大程度的解决内存泄露问题

5. ThreadLocal真的会导致内存泄露?

  • Entry对象中的key设置成弱引用,并且使用get、set或remove方法清理key为null的value值,并不能彻底解决内存泄露问题

如果ThreadLocal变量有很多key为null的Entry对象,但是一直没有调用其他ThreadLocal变量的get,set,remove方法,导致value值一直存在,这样还是会存在内存泄露的问题

6. 如何解决内存泄露问题?

在使用完ThreadLocal对象后,调用remove方法

  • 先创建一个CurrentUser类,其中包含了ThreadLocal的逻辑。
  • 在finally代码块中,调用remove方法清理没用的数据。如果业务代码出现异常,也能及时清理没用的数据。