ThreadLocal -- 03 -- ThreadLocal源码解析

原文链接:ThreadLocal – 03 – ThreadLocal源码解析


相关文章:


ThreadLocal,用于提供线程局部变量,常用于多线程环境下对非线程安全资源的并发访问,如果我们不想加锁,就可以使用 ThreadLocal 来使得每个线程都持有一个该资源的副本,各个线程之间互不干扰

ThreadLocal 与各种锁机制相比,侧重于以空间换时间,来提高并发效率


一、内部类解析

  • SuppliedThreadLocal<T>

    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
    
        // 供给函数
        private final Supplier<? extends T> supplier;
    
        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }
    
        // 获取初始值    
        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }
    
    • SuppliedThreadLocal 是 ThreadLocal 的内部类,其继承自 ThreadLocal,用于从指定的供给函数中获取初始值
  • ThreadLocalMap

    • ThreadLocalMap 是 ThreadLocal 的内部类,是一个自定义的哈希映射,用于维护线程的局部变量

    • ThreadLocalMap 所有方法的访问权限都是私有的,因此只有 ThreadLocal 可以调用 ThreadLocalMap 中的方法,而其他类则不可以

    • ThreadLocalMap 的访问权限是包级别的,也就是同一个包下面的类可以访问 ThreadLocalMap,但是不能调用 ThreadLocalMap 中的方法


二、字段解析

  • threadLocalHashCode

    private final int threadLocalHashCode = nextHashCode();
    
    • ThreadLocal 的哈希值,通过调用 nextHashCode() 方法计算得到
  • nextHashCode

    private static AtomicInteger nextHashCode =
            new AtomicInteger();
    
    • 下一个哈希值,使用 AtomicInteger 类进行原子更新
  • HASH_INCREMENT

    private static final int HASH_INCREMENT = 0x61c88647;
    
    • 哈希值增量,每当创建一个 ThreadLocal,nextHashCode 就会增长魔数 0x61c88647

    • 魔数 0x61c88647 十分特殊,被称之为斐波那契数,或黄金分割数,用于使散列更加均匀,减少哈希冲突

      public static void main(String[] args) {
          long l1 = (long) ((1L << 32) * (Math.sqrt(5) - 1)/2);
          System.out.println("32 bit unsigned: " + l1);
          int i1 = (int) l1;
          System.out.println("32 bit signed:   " + i1);
          System.out.println("0x61c88647 = " + 0x61c88647);
          
          // 32 bit unsigned: 2654435769
          // 32 bit signed:   -1640531527
          // 0x61c88647 = 1640531527
      }
      
    • 举例说明

      public class ThreadLocalTest {
      
          private static final int INITIAL_CAPACITY = 16;
          private static final int HASH_INCREMENT = 0x61c88647;
          private static AtomicInteger nextHashCode = new AtomicInteger();
      
          private static int nextHashCode() {
              return nextHashCode.getAndAdd(HASH_INCREMENT);
          }
      
          private static void hashCodeTest(int len) {
              for (int i = 0; i < len; i++) {
                  int bucket = nextHashCode() & (INITIAL_CAPACITY - 1);
                  System.out.println("【" + i + "】在桶中的位置:【" + bucket +"】");
              }
          }
      
          public static void main(String[] args){
              hashCodeTest(16);
              
              // 【0】在桶中的位置:【0】
              // 【1】在桶中的位置:【7】
              // 【2】在桶中的位置:【14】
              // 【3】在桶中的位置:【5】
              // 【4】在桶中的位置:【12】
              // 【5】在桶中的位置:【3】
              // 【6】在桶中的位置:【10】
              // 【7】在桶中的位置:【1】
              // 【8】在桶中的位置:【8】
              // 【9】在桶中的位置:【15】
              // 【10】在桶中的位置:【6】
              // 【11】在桶中的位置:【13】
              // 【12】在桶中的位置:【4】
              // 【13】在桶中的位置:【11】
              // 【14】在桶中的位置:【2】
              // 【15】在桶中的位置:【9】
          }
      }
      
      • 如上所示,数据在哈希桶数组中散列地很均匀

三、构造方法解析

  • ThreadLocal()

    public ThreadLocal() {
    }
    
    • 无参构造

四、方法解析

1、nextHashCode() 相关方法
  • nextHashCode()

    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    
    • 计算下一个哈希值,使用 AtomicInteger 类进行原子更新

2、get() 相关方法
  • get()

    public T get() {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取与 ThreadLocal 相关联的 ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 根据 key 获取 Entry 对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            // 如果 Entry 对象不为 null
            if (e != null) {
                @SuppressWarnings("unchecked")
                // 获取 value 并返回
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
  • getMap(Thread t)

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    • 获取与 ThreadLocal 相关联的 ThreadLocalMap
  • setInitialValue()

    private T setInitialValue() {
        // 获取线程局部变量的初始值
        T value = initialValue();
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取与 ThreadLocal 相关联的 ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        // 如果 map 不为 null
        if (map != null)
            // 设置与 key 关联的 value
            map.set(this, value);
        // 如果 map 为 null
        else
            // 创建与 ThreadLocal 相关联的 ThreadLocalMap
            createMap(t, value);
        return value;
    }
    
    • 设置线程局部变量的初始值
  • initialValue()

    protected T initialValue() {
        return null;
    }
    
    • 返回线程局部变量的初始值,默认返回 null

    • 如果不希望线程局部变量的初始值为 null,我们可以通过继承 ThreadLocal 并重写 initialValue() 方法来设定初始值

  • createMap(Thread t, T firstValue)

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    • 创建与 ThreadLocal 相关联的 ThreadLocalMap

3、withInitial(Supplier<? extends S> supplier) 相关方法
  • withInitial(Supplier<? extends S> supplier)

    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }
    
    • 创建线程局部变量,变量的初始值由 Supplier 供给函数提供

4、set(T value) 相关方法
  • set(T value)

    public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取与 ThreadLocal 相关联的 ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        // 如果 ThreadLocalMap 不为 null
        if (map != null)
            // 设置与 key 关联的 value
            map.set(this, value);
        // 如果 ThreadLocalMap 为 null
        else
            // 创建与 ThreadLocal 相关联的 ThreadLocalMap
            createMap(t, value);
    }
    
    • 设置线程局部变量的初始值为指定 value

5、remove() 相关方法
  • remove()

     public void remove() {
         // 获取与 ThreadLocal 相关联的 ThreadLocalMap
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             // 删除 key 对应的 Entry 
             m.remove(this);
     }
    
    • 删除线程局部变量的当前值

6、createInheritedMap(ThreadLocalMap parentMap) 相关方法
  • createInheritedMap(ThreadLocalMap parentMap

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
    
    • 创建与 InheritableThreadLocal 相关联的 ThreadLocalMap

    • 该方法仅在 Thread 类的构造方法中被调用


7、childValue(T parentValue) 相关方法
  • childValue(T parentValue)

    T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }
    
    • 该方法在子类 InheritableThreadLocal 中被显示定义

    • 此处定义主要是为在 ThreadLocalMap 构造方法 ThreadLocalMap(ThreadLocalMap parentMap) 中调用该方法提供方便,通过多态来直接调用子类 InheritableThreadLocal 中的 childValue(T parentValue) 方法


五、内存泄漏问题

  • ThreadLocalMap 以 ThreadLocal 的弱引用作为 key,而弱引用在 GC 时会被回收,这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry,此时我们就无法访问这些 key 为 null 的 Entry 的 value,如果线程生命周期很长,这些 value 就会不断积累,且不会被 GC 回收 (value 存在一条强引用链:Thread -> ThreaLocalMap -> Entry -> value),最终造成内存泄漏

  • 因此想要正确地使用 ThreadLocal,避免内存泄漏,我们需要显示地调用 remove() 方法来清除数据


六、举例说明

  • ThreadLocalTest.java

    public class ThreadLocalTest {
    
        private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    
        private static void threadLocalTest() {
            threadLocal.set(123);
            System.out.println(threadLocal.get());
            threadLocal.remove();
        }
    
        public static void main(String[] args){
            threadLocalTest(); // 123
        }
    }
    

七、参考资料

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值