ThreadLocal

结构

每个Thread内部都有一个独立的threadLocals,其维护着一个table[]数组
把每个ThreadLocal对象的(hashcode&len-1)作为key找到对应的table[]所在位置,set到线程内部的threadLocals,这样保持线程之间资源独立

threadLocals内部的table每个对象是一个WeakReference类型entry弱引用在没有强引用指向对象的时候,会在gc的时候回收对象,这样key就会变成null,而value和value指向的对象还是存在强引用关系(ThreadLocal自带了解决方案是每次remove/set/get的时候都会将key==null的entry删除)

防止线程池串线程问题

  1. 直接使用ThreadLocal,每次init()或者destroy()时,主动remove()
  2. 使用InheritableThreadLocal,

防止NullPointException

  1. 重写 threadLocal的initialValue()方法,此方法是一个延迟加载方法
  2. 如果不想每次get()set(),就重写initialValue
  3. 返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次回使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法
  4. 推荐使用 private static来存储ThreadLocal,减少不必要的创建开销
    注意:
  • 使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。因为ThreadLocal使用弱引用,不主动删除,
    ThreadLocal其实存在引用,会导致ThreadLocal无法被自动回收
  • 如果是static的ThreadLocal,一般不需要调用remove(这里只是延长了生命周期)个人建议用完还是调用remove删除

ThreadLocal 内存泄漏问题(存在但是基本不会碰上)

泄漏案例1
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。

其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。

但是这些被动的预防措施并不能保证不会内存泄漏:

  • 使用staticThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏内存泄漏分析并且使用了线程池,而每个线程如果不回收value,就会保存在内存,如果每个线程持有5MB的数据,共200个线程就会占用1GB的内存
  • 分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏

你可能感兴趣的:(ThreadLocal)