ThreadLocal源码解析

本文深入解析了ThreadLocal的工作原理,包括其实例字段、实例方法、静态方法等,并详细阐述了其内部类ThreadLocalMap的结构及操作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ThreadLocal<T>

实例字段
  • threadLocalHashCode

    private final int threadLocalHashCode = nextHashCode();
    

    每个ThreadLocal实例的hashCode。用于将ThreadLocal变量插入到线程中的一个哈希表中(线程的ThreadLocalMap字段)

实例方法
  • protected T initialValue():默认实现,返回null
    • 一般情况下,当线程第一次调用ThreadLocalget()时,会调用该方法
    • 若线程在先前有调用set()方法,则第一次调用get()时不会调用该方法
    • 一般情况下,该方法对每个线程来说,最多只会调用一次
    • 但是,在调用remove()后再调用get(),会导致该方法再次被调用
    • 通常来说,线程不期望一个null的初始值,ThreadLocal一般会被重写,典型地,会使用一个匿名内部类
  • public T get()
    • 首先获取当前线程Thread t = Thread.currentThread()
    • 然后获取这个线程的ThreadLocalMap实例map
    • map不为空,以当前ThreadLocal实例对象为key,去map中取值,若有值,则直接return
      • map为空,或map中没值,则return setInitialValue()
  • private T setInitialValue()
    • 先调用initialValue()T value = initialValue()
    • 获取当前线程t中的ThreadLocalMap对象map
    • map不为空,以当前ThreadLocal对象为key,调用map.set(this,value)
    • map为空,调用createMap(t,value),为线程t创建ThreadLocalMap实例
    • return value
  • public void set(T value)
    • 获取当前线程tThreadLocalMap对象map
    • map不为空,以当前ThreadLocal对象为key,调用map.set(this,value)
    • map为空,调用createMap(t,value),为线程t创建ThreadLocalMap实例
  • public void remove()
    • 获取当前线程tThreadLocalMap对象map
    • map不为空,调用该mapremove(this)方法
静态方法
  • public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)

    return new SuppliedThreadLocal<>(supplier);

    返回一个ThreadLocal,其初始值由Supplier来决定

  • private static int nextHashCode()

    return nextHashCode.getAndAdd(HASH_INCREMENT);

    生成ThreadLocal对象的哈希值

静态字段
  • private static AtomicInteger nextHashCode = new AtomicInteger();

    用来生成ThreadLocal的HashCode

  • private static final int HASH_INCREMENT = 0x61c88647;

    HashCode生成的增量

静态内部类

ThreadLocalMap

  • 静态内部类Entry

    static class Entry extends WeakReference<ThreadLocal<?>>{
        Object value;
        Entry(ThreadLocal<?> k,Object v){
            super(k);
            value = v;
        }
    }
    
  • 静态字段

    • int INITIAL_CAPACITY = 16
  • 实例字段

    • Entry[] table
    • int size = 0 :Entry数组里的元素个数
    • int threshold:对Entry数组进行resize的阈值
  • 实例方法

    • 构造方法

      ThreadLocalMap(ThreadLocal<?> firstKey,Object firstValue){
          table = new Entry[INITIAL_CAPACITY];
          int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY-1);
          table[i] = new Entry(firstKey,firstValue);
          size = 1;
          setThreshold(INITIAL_CAPACITY); //这里会设置负载因子为 2/3
      }
      
    • void setThreshold(int len)

      threshold = len * 2 / 3;
      
    • Entry getEntry(ThreadLocal<?> key)

      //获取线程相关的ThreadLocal时,会调用这个方法
      //使用Hash来定位该ThreadLocal在线程的map中的位置
      int i = key.threadLocalHashCode & (table.length - 1);
      Entry e = table[i];
      if(e != null && e.get() == key)
          return e;
      else
          return getEntryAfterMiss(key,i,e);
      
    • Entry getEntryAfterMiss(ThreadLocal<?> key,int i,Entry e)

      Entry[] tab = table;
      int len = tab.length;
      while(e != null){
          ThreadLocal<?> k = e.get();
          if(k == key)
              return e;
          if(k == null)
              expungeStaleEntry(i);
          //该Entry不为null,但是该Entry的key,为null
          //key是一个WeakReference<ThreadLocal<?>>
          //是会被gc回收的
          //而value不是WeakReference
          //此时应该移除该Entry
          else
              i = nextIndex(i,len);  //线性探测法,每次+1
          //如果当前k不为空,且不等于key,说明发生冲突,则按冲突解决方法向后探测
          //探测成功则返回成功结果,否则返回null
          e = tab[i];
      }
      return null;
      
    • int expungeStaleEntry(int staleSlot)

      Entry[] tab = table;
      int len = tab.length;
      
      // expunge entry at staleSlot
      tab[staleSlot].value = null;
      tab[staleSlot] = null;
      size--;
      
      // Rehash until we encounter null
      Entry e;
      int i;
      for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
         ThreadLocal<?> k = e.get();
         if (k == null) {
             e.value = null;
             tab[i] = null;
             size--;
         } else {
             int h = k.threadLocalHashCode & (len - 1);
             if (h != i) {
                 tab[i] = null;
                 while (tab[h] != null)
                     h = nextIndex(h, len);
                     ab[h] = e;
                 }
              }
        }
      return i;
      
    • private void rehash()

      expungeStaleEntries(); //先移除所有过时的Entry
      if(size >= threshold - threshold / 4)  //若移除后仍然超过某个阈值
          resize();   //扩容
      
    • private void resize()

      扩容为原来的2倍,然后重新Hash

    • private void expungeStaleEntries()

      对整个Entry数组进行遍历,依次调用expungeStaleEntry()

总结

  • 核心在于ThreadLocal的静态内部类ThreadLocalMap,在这个ThreadLocalMap类的内部,维护了一个Entry数组,这个Entry数组实际是一个哈希表,Entry作为ThreadLocalMap的静态内部类,其结构类似于Map,一个Entry对象持有一个ThreadLocal弱引用,为key,一个Object,作为value
  • 在创建一个ThreadLocal对象时,会为对象生成一个threadLocalHashCode,会在插入到线程的ThreadLocalMap时使用
  • 每个线程(Thread)内部维护了一个哈希表(ThreadLocalMap),这个哈希表中存储了该线程的所有线程局部变量(以ThreadLocal为key,Object为value)
  • 在某一线程内调用ThreadLocalset(T t)方法,会把t加入到该线程的ThreadLocalMap中,以当前ThreadLocal对象为key,t为value
  • 获取线程局部变量时,在当前线程中直接调用ThreadLocalget()方法,即会以当前ThreadLocal变量为key,去当前线程的map中寻找对应的value

备注:

  • 每个ThreadLocal实例对象的hashCode生成规则是不断累加一个数0x61c88647,这个数有点神奇,不断累加产生的hash值分布非常均匀
  • ThreadLocal对象插入到线程的哈希表中时,将ThreadLocal实例对象的hashCode与线程的ThreadLocalMap中的Entry数组大小做“与”操作,生成该ThreadLocalEntry数组中的位置,插入到Entry数组中
  • ThreadLocal的冲突解决方案采用的是线性探测,每次向后探测一个位置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值