ThreadLocal

ThreadLocal是用在多线程的场景的

  • 由于ThreadLocal的特性,同一线程在某地方进行设置,在随后的任意地方都可以获取到。从而可以用来保存线程上下文信息。

  • 常用的比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。

  • 还有比如Spring的事务管理,用ThreadLocal存储Connection,从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。

每个线程往ThreadLocal中读写数据是线程隔离,互相之间不会影响的,所以ThreadLocal无法解决共享对象的更新问题!

ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

ThreadLocal 源码分析

每个线程都将自己维护一个 ThreadLocal.ThreadLocalMap 类在上下文中; 所以, ThreadLocal 的 set 方法其实是将 target value 放到当前线程的 ThreadLocalMap 中, 而 ThreadLocal 类自己仅仅作为该 target value 所对应的 key:

  • Thread类
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  • ThreadLocal的set/get
 public void set(T value) {
    Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null)
          map.set(this, value);
      else
          createMap(t, value);
  }
  public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

谨防 ThreadLocal 导致的内存泄露和 OOM
ThreadLocal.ThreadLocalMap 类中维护了的一个自定义数据结构 Entry,Entry 类继承了弱引用 WeakReference, 更具体的说, Entry 中的 key (ThreadLocal 类型) 使用弱引用, value 依旧使用强引用

为什么采用WeakReference

一、内存泄漏原因探索

假设 Entry 不继承 WeakReference, 令 key 也使用强引用, 那么结合上一节的内容, 只要该 thread 不退出, 通过 Thread -> ThreadLocal.ThreadLocalMap -> key 这条引用链, 该 key 就可以一直与 gc root 保持连通; 这时即便在外部这个 key 对应的 threadLocal 已经没有有效引用链了, 但只要该 thread 不退出, jvm 依旧会判定该 threadlocal 不可回收。
.
于是尴尬的事情发生了: 由于 ThreadLocal.ThreadLocalMap 这个内部类没有对外暴露 public 方法, 在 Thread 类里面 ThreadLocal.ThreadLocalMap 也是 package accessible 的, 这意味着我们已经没有任何方法访问到该 key 对应的 value 了, 可它就是无法被回收, 这便是一个典型的内存泄露。
.
而如果使用 WeakReference 这个问题就解决了: 当该 key 对应的 threadlocal 在外部已经失效后, 便仅存在 thread 里的 weak reference 指向它, 下次 gc 时这个 key 就会被回收掉。

二、怎么解决这个内存泄漏问题

如果某些 threadlocal 设置的 value 是大对象, 而所涉及的 thread 却没来得及在 threadlocal 被 gc 前作 remove, 再加上之后也没有什么其他 threadlocal 去作 get / set 操作, 那这些大对象是没机会被回收的, 这将造成严重的内存泄露甚至是 OOM。所以使用 ThreadLocal 要谨记一点: 用完主动 remove, 主动释放内存, 而且是放在 finally 块里面 remove, 以确保执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值