当前位置:网站首页>多线程通信之ThreadLocal

多线程通信之ThreadLocal

2022-01-15 02:31:37 ordinaryBlog

ThreadLocal作用

使线程中的某个值,与保存值的对象关联起来。通常用于防止可变的单实例变量或全局变量进行共享

应用场景

  • 每个线程需要一个共享的对象(通常是工具类)
  • 每个线程内需要保存全局变量

运行过程

在ThreadLocal第一次调用get方法时,就会调用initialValue来获取初始值。
可以通过set方法存入ThreadLocal

优点

  • 达到线程安全
  • 不需要加锁,提高执行效率
  • 更高效的利用内存,节省开销
  • 免去传参的繁琐

ThreadLocal原理:

initialValue()

  • 该方法会返回当前线程对应的“初始值”,这是一个延迟加载的方法,只有在调用get的时候才会触发
  • 当线程第一次使用get方法访问变量时,才会调用此方法,除非线程先调用了set方法,在这种情况下,不会为线程调用本initialValue方法
  • 通常,每个线程最多调用一次此方法,但如果已经调用了remove()后,再次调用get(),则可以再次调用此方法。
  • 如果不重写本方法,这个方法会返回null, 一般使用匿名内部类的方法来重写initialValue方法,以便在后续的使用中可以初始化副本对象

get()

  • get方法是先取出当前线程的ThreadLocalMap,然后调用map.getEntry方法,把本ThreadLocal的引用作为参数传入,取出map中属于本ThreadLocal的value
  • 注意,这个map以及map中的key和value都是保存线程中的,而不是保存在ThreadLocal中

ThreadLocalMap类

  • TheadLocalMap类是每个线程Thread类里面的变量,里面最重要的是一个键值对数组Entry[] table,可以认为是一个map,键值对:key:这个ThreadLocal value:实际需要的成员变量
  • 内存冲突问题:ThreadLocalMap使用的是线性探测法,也就是如果发生冲突,就继续寻找下一位置,而不是用链表拉链

ThreadLocalMap中的value泄露

  • ThreadLocalMap的每个Entry都是对key的弱引用,同时,每个Entry都包含了一个对value的强引用。正常情况下,当线程终止,保存在Threadlocal中的value会被垃圾回收,因为没有任何强引用了,但是,如果线程不终止(比如线程需要保持很久),那么key对应的value就不能被回收。
  • 存在 Thread—>ThreadLocalMap—>Entry(key为null)—>value
  • 因为value和Thread之间还存在强引用链路,所以导致value无法被回收,就可能会出现oom
  • JDK已经考虑到了这个问题,所以在set,remove,rehash方法中会扫描key为null的Entry,并将其对应的value设为null,这样value对象就可以被回收
  • 如果一个ThreadLocal不被使用,那么实际上set,remove也不会被调用,如果同时线程又不停止那么就导致了value的内存泄露

如何避免 内存泄露

调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,应该调用remove方法

ThreadLocal注意点

  • 空指针异常:在进行get之前,必须先set,否则可能会报空指针异常
  • 共享对象:如果在每个线程中ThreadLocal.set进去的本来就是多线程共享同一个对象,比如static对象,那么多个线程的ThreadLocal.get取得的还是这个共享对象本身,还是会用并发访问问题
  • TheadLocal变量类似于全局变量,他能够降低代码的可重用性,并在类之间引入隐含的耦合性,因此在使用时要格外的小心

版权声明
本文为[ordinaryBlog]所创,转载请带上原文链接,感谢
https://blog.csdn.net/kang2411212/article/details/120138295

随机推荐