ThreadLocal 源码全详解(ThreadLocalMap)

1. ThreadLocal 源码分析

 

1.1 ThreadLocal 原理

首先我们得从 Thread 类讲起,在 Thread 类中有维护两个 ThreadLocal.ThreadLocalMap 对象(初始为 null,只有在调用 ThreadLocal 类的 set 或 get 时才创建它们):threadLocals 和 inheritableThreadLocals。也就是说每个 Thread 对象都有两个 ThreadLocalMap 对象,ThreadLocalMap 是 ThreadLocal 定制的 HashMap,是 ThreadLocal 的内部类,其 key 为弱引用的 ThreadLocal 对象,value 为对应设置的 Object 对象。

public class Thread implements Runnable {
    //......
    //与此线程有关的ThreadLocal值。由ThreadLocal类维护
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    //......
}

我们想设置 ThreadLocal 值时,通过查看源码我们可以发现,使用 ThreadLocal 的 set() 方法时实际是调用了当前线程的 ThreadLocalMap 的 set() 方法。ThreadLocal 的 set() 方法中,先用 Thread.currentThread() 获得当前线程对象 t ,通过当前线程对象 t 获取线程的 ThreadLocalMap 对象 map ,接着判断 map 是否为 null——为 null 则调用creadMap() 方法传入当前线程对象 t 和当前 set() 方法的入参 value 创建为当前线程创建 ThreadLocalMap 对象并 put value 添加变量;不为 null 则调用 map.set(value) 设置该 ThreadLocal 对象的值。

// ThreadLocal.java
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

由此可见,变量是放在当前线程的 ThreadLocalMap 中,而 ThreadLocal 是 ThreadLocalMap 的封装,传递了变量值。

1.2 ThreadLocalMap 原理

ThreadLocalMap 的数据结构实际上是数组,对比 HashMap 它只有散列数组没有链表。

1.2.1 ThreadLocalMap 的四个属性

  • Entry[] table
  • INITIAL_CAPACITY
  • size
  • threshold
// 源码
static class ThreadLocalMap {
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    //初始容量默认为16,必须是2的幂
    private static final int INITIAL_CAPACITY = 16;

    // table每次resized,容量都得是2的幂
    private Entry[] table;

    // 当前table中的存放的元素数量
    private int size = 0;

    // 扩容阀值
    private int threshold; // Default to 0
	
    /**
     * 接下来还有 set()、get()、扩容方法、expungeStaleEntry()、cleanSomeSlots()等重要方法就不贴源码了
	 * ......
     */
} 

1.2.2 Hash 算法

ThreadLocalMap 实现了自己的hash 算法来解决散列表数组冲突。

int i = key.threadLocalHashCode & (len - 1);

这里的`i` 就是当前 key 在散列表中对应的数组下标位置。`len` 指的是`ThreadLocalMap` 当前的容量`capacity`。
而比较重要的是我们必须知道`key.threadLocalHashCode` 这个值是怎么计算的?
通过源码可以知道`threadLocalHashCode` 是`ThreadLocal` 的一个属性,其值是调用`ThreadLocal` 的`nextHahCode()` 方法获得的。
`nextHashCode()`:返回`AtomicInteger nextHahCode` 的值,并将`AtomicInteger nextHahCode`  自增一个常量值——`HASH_INCREMENT(0x61c88647)`。
> __特别提醒__:每创建一个`ThreadLocal` 对象(每将对象 hash 到 map 一次),`ThreadLocal.nextHashCode` 就增长`0x61c88647`。(`0x61c88647` 是斐波那契数,使用该数值作为 hash
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值