ThreadLocal原理

ThreadLocal原理

Thread类里维护了一个ThreadLocalMap,这个map的key是ThreadLocal,所有说一个Thread里可以有多个ThreadLocal

  • ThreadLocal作用:

    提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度

  • 常用方法:

    • ThreadLocal():构造方法
    • public void set(T value):给当前线程绑定局部变量
    • public T get():取出当前线程绑定的局部变量
    • public void remove():删除当前线程绑定的局部变量
  • ThreadLocal与synchronized的区别:

    • ThreadLocal相当于是空间换时间,为每一个线程提供一份变量的副本,从而实现同时访问而相互不干扰
    • synchronized则是时间换空间,只有一份变量,让不同线程排队访问
    • 时间带来的是性能的损耗,并发性能的降低(一个一个等着取东西)
  • ThreadLocal内部设计:

    • 每个Thread内部都有一个ThreadLocalMap,这个map由ThreadLocal来维护,由ThreadLocal向map中设置和获取线程的变量值
    • Map里面存储ThreadLocal对象(key),线程的变量副本(value)
    • 对于不同的线程的副本值形成了隔离,互不干扰
  • 看源码:

    • set():

      public void set(T value) {
          //通过当前线程来取ThreadLocalMap
              Thread t = Thread.currentThread();
              ThreadLocalMap map = getMap(t);
          //判断map是否存在
              if (map != null) {
                  map.set(this, value);
              } else {
                  createMap(t, value);
              }
          
          }
      
      ThreadLocalMap getMap(Thread t) {
              return t.threadLocals;//这个threadLocals就是维护的ThreadLocalMap
          }
      
      void createMap(Thread t, T firstValue) {
              t.threadLocals = new ThreadLocalMap(this, firstValue);
          }
      
    • get():

      public T get() {
             //通过当前线程来取ThreadLocalMap
              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;
                  }
              }
          //没有map 或 有map没取到value
              return setInitialValue();
          }
      private T setInitialValue() {
              T value = initialValue();//默认是给空值,可重写
          
              Thread t = Thread.currentThread();
              ThreadLocalMap map = getMap(t);
          
              if (map != null) {
                  map.set(this, value);
              } else {
                  createMap(t, value);
              }
          
              if (this instanceof TerminatingThreadLocal) {
                  TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
              }
          
              return value;
          }
      
      protected T initialValue() {
              return null;
          }
      
    • remove():

       public void remove() {
               ThreadLocalMap m = getMap(Thread.currentThread());
               if (m != null) {
                   m.remove(this);
               }
           }
      private void remove(ThreadLocal<?> key) {
                  Entry[] tab = table;
                  int len = tab.length;
                  int i = key.threadLocalHashCode & (len-1);
                  for (Entry e = tab[i];
                       e != null;
                       e = tab[i = nextIndex(i, len)]) {
                      if (e.refersTo(key)) {
                          e.clear();
                          expungeStaleEntry(i);
                          return;
                      }
                  }
              }
      
    • ThreadLocalMap(是Thread的一个属性):

      key是当前ThreadLoal对象的引用

      value是Entry:

       static class Entry extends WeakReference<ThreadLocal<?>> {
               //Entry继承了弱引用,本身也是个键值对
                  Object value;
      
                  Entry(ThreadLocal<?> k, Object v) {
                      super(k);
                      value = v;
                  }
              }
      
  • 弱引用相关:

    • 内存溢出:就是内存不够用了

    • 内存泄漏:已分配出去的内存,因为某些原因无法GC了,那就GG了

    • 弱引用:垃圾回收器一旦发现具有弱引用的对象,不管当前内存空间足够与否,都会进行GC

    • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 使用完ThreadLoacl,ThreadLoacl ref被回收,key又是弱引用,所以ThreadLoacl可以被GC

    • 但是如果没有手动删除这个Entry以及CurrentThread还在运行的前提下,存在强引用链Thread Ref -> current thread-> ThreadLoaclMap -> Entry -> value,value不会被GC,且此时ThreadLoacl已经被GC,key为null,意味着value无法被访问,value这块内存就是泄露了

    • 内存泄漏的原因是:

      1. 使用完删除Entry
      2. CurrentThread依然运行
    • 内存泄漏的根源:

      ThreadLocalMap是Thread的一个属性,所以两者的生命周期一样长,如果没有手动删除Entry就会内存泄漏

    • 但是但是:

      • ThreadLocalMap内部的set/get/remove方法中,会对key(ThreadLocal)为null的情况进行判断,如果key为null,会对value赋值为null,这意味着就算你使用完没删除Entry,在你下一次调用set/get/remove方法时,会清楚value,避免内存泄漏,这是一种保障,也是使用弱引用的优势
  • Hash冲突:

    ThreadLocalMap使用线性探测法来解决Hash冲突:

    一次探测下一个地址,直到空的地址插入,若整个空间都找不到空余的地址,就产生溢出

    可以把Entry[ ] table看成是一个环形数组
    value,避免内存泄漏,这是一种保障,也是使用弱引用的优势

  • Hash冲突:

    ThreadLocalMap使用线性探测法来解决Hash冲突:

    一次探测下一个地址,直到空的地址插入,若整个空间都找不到空余的地址,就产生溢出

    可以把Entry[ ] table看成是一个环形数组

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值