多线程高并发学习之ThreadLocal

ThreadLocal介绍:

  • ThreadLocal提供了线程的局部变量,让每个线程都可以通过get/set方法来对局部变量的数据进行操作,不会和其他线程的局部变量产生冲突,实现了线程的数据隔离,比如超市的公共储物柜,每个人都可以使用,但是每个人的物品有都是分隔开来的

ThreadLocal实现原理:

  1. 通过源码可以观察到,当调用ThreadLocal的set方法时,去调用了getMap方法,传入了当前线程,返回了ThreadLocalMap

    image

    • 这个ThreadLocalMap其实就是存在于Thread类里面的一个Map
  2. 获取到ThreadLocalMap之后,将自己(ThreadLocal)作为key,值作为value放入ThreadLocalMap中,这样就能起到线程之间数据隔离的效果

image

提问时间:

Thread有什么用呢?或者说应用场景在哪?

  • 在项目中,ThreadLocal确实 很(根)少(本)用(不)到(用),在Spring中有ThreadLocal的实现,ThreadLocal存储的是一个Map,Map里面是一个个的Entry,在Spring中实现中,Entry中的Key存放的是DataSource,Value存放的是Connection,用ThreadLocal是为了保证一个线程获取一个Connection连接对象,从而就能保证一次事务内所有的操作都是在同一个数据库连接上

刚刚提到ThreadLocalMap是定义在Thread中的,那么我可以在ThreadLocal中定义这个Map,将当前线程作为key,值作为value,不也能实现线程之间数据隔离吗?如下图:

image

  • 理论上来说是可以的,但是要考虑以下三点

    1. 每个线程都会有多个私有变量,既然要以当前线程为Key,那么就要区分多个变量,需要维护一个标识
    2. 就算解决了维护标识的问题,还有就是当并发足够大时,多个线程来访问自己存储的私有变量时,多个线程访问这个Map,可能会导致Map体积膨胀,从而导致Map的访问性能有所下降
    3. 而且这个Map维护着所有线程的私有变量,所以不知道什么时候可以销毁它

为什么key要用弱引用指向ThreadLocal对象?什么是弱引用?

  • 首先,先介绍一下Java的四中引用:Java有强、软、弱、虚四中引用

    1. 强引用(NormalReference):强引指向一个对象时,只要对象没有被置为null,那么在GC时就不会被回收
    2. 软引用(SoftReference):当有足够的内存时,这个引用指向的这个对象就不会被回收,当内存不足时,这块引用指向的对象就会被回收
    3. 弱引用(WeakReference):只要触发GC,这个引用指向的对象就会被回收
    4. 虚引用(PhantomReference):虚引用主要的作用是跟踪垃圾回收的状态,当回收时通过引用队列做一些通知类的工作【迷迷糊糊,不太理解】
  • 其次我们来看,为什么要用弱引用指向ThreadLocal对象呢

    • 假设key用的是强引用指向的是ThreadLocal对象,那么当Thread与ThreadLocal之间的引用断掉,又因为ThreadLocalMap是存在于Thread中,只要Thread不消失,这个ThreadLocalMap就不会消失,ThreadLocalMap中这个entry就会一直存在从而导致内存泄漏
    • 现在是key用的弱引用指向的ThreadLocal对象,当Thread与ThreadLocal之间的引用断掉,key与ThreadLocal之间的引用就会自动消失,不会出现内存泄漏的问题
  • 但是ThreadLocal还是会有内存泄露的问题出现,当ThreadLocal对象被回收,key的值变为了null,导致相对应的value再也无法被访问到,因此还是会出现内存泄漏的问题,小伙伴不要把 内存泄漏 和 内存溢出(OOM)搞混了

    • 内存泄漏:有一块内存不用了,但是永远无法被回收
    • 内存溢出(OOM):内存越申请越多,越申请越多,最后内存被占满,无法再申请到内存了,就会出现内存溢出OOM

刚刚说ThreadLocal会出现内存泄漏,那么会导致内存长期泄漏吗?

  • 这个是不会的,因为ThreadLocalMap是属于Thread的,只要将Thread销毁,ThreadLocalMap也会跟着被销毁,而且ThreadLocal也有一些“保护”措施,就是在操作ThreadLocal时,发现key为null时,会将其清除掉,所以大家不用担心会出现长期性内存泄漏问题
  • 那么长期性的内存泄漏是在什么条件下产生的呢,必须要同时满足以下几点才会导致长期性的内存泄漏

    1. ThreadLocal被回收
    2. 线程复用(线程池的环境下)
    3. 线程复用后不再调用ThreadLocal的set/get/remove方法

说了这么多,那么,你学废了吗?
不点个赞再走嘛
image

你可能感兴趣的:(java)