ThreadLocal 源码解析

引言

ThreadLocal 可以在每个线程中存取数据,并且不同的线程中的数据互不影响。使用在数据以线程为作用域并且不同的线程拥有不用的数据副本,或者是复杂的参数传递时(参数在同一线程中的不同类中传递)。

在分析消息机制源码的时候,涉及到了 ThreadLocal,使用是在 Looper 类中通过 ThreadLocal 对象的 set 方法和 get 方法存取当前线程的 Looper 对象

我们发现 ThreadLocal 对象是一个静态的对象,说明每个线程都可以通过该对象来存取当前线程对应的 Looper ,说明 ThreadLooper 中存放的数据确实是以线程为作用域的,源码接着看

源码分析

看源码之前,先大体的说一下工作的原理。每个线程类 Thread 中都保存了一个 ThreadLocalMap ,可以理解为就是一个 Map,Map 的 key 就是这个 Thread 中所有使用到的 ThreadLocal 对象,Map 的 value 是 Thread 中使用该 ThreadLocal 存储的数据的值。在使用时通过 ThreadLocal 即可存取对应的 value。

可以这么理解。但是 ThreadlocalMap 并不是一个 Map,其内部通过一个 Entry 类型的数组 Entry[] 来存储 ThreadLocal 和其存储的值,Entry 中保存了 value 并且 Entry 持有 ThreadLocal 的弱引用,Entry 数组中的索引是通过 ThreadLocal 的 hashCode 计算的值,这样根据 ThreadLocal 的 hashCode 也就有了与 Entry[] 中每个索引位置的值之间的关系。

ThreadLocalMap 存在的意义不仅仅是保存 Entry[] 数组,也为该数组中数据的存取提供了更多计算方法

// Entry 中只保存了 value
static class Entry extends WeakReference<ThreadLocal> {
    /** The value associated with this ThreadLocal. */
    Object value;
        Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}

set() 方法用于数据的存储

// ThreadLocal 的 set() 方法
public void set(T value) {
    Thread t = Thread.currentThread(); // 获取当前线程
    ThreadLocalMap map = getMap(t); // 从线程中取出 ThreadLocalMap
    if (map != null)
        map.set(this, value); // 如果 ThreadLocalMap 不为 null ,则使用 ThreadLocalMap 存储
    else
        createMap(t, value); // 如果 ThreadLocalMap 为 null,则为该线程初始化 ThreadLocalMap ,并将数据存储
}


// ThreadLocal 的 getMap() 方法
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}


// ThreadLocalMap 的 set() 方法
private void set(ThreadLocal key, Object value) {

    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1); // 根据 ThreadLocal 计算在 Entry[] 数组中的索引值

    // 如果该索引位置有值,则判断 ThreadLocal 是否匹配,不匹配则遍历到下一有值位置
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { 
        ThreadLocal k = e.get();

        // 如果有值则判断当前位置的 ThreadLocal 和需要插入的 ThreadLocal 是否相同,如果相同则直接修改 value 为新值
        if (k == key) { 
            e.value = value;
            return;
        }

        // 如果对应位置 ThreadLocal 为空,则取代旧的 Entry ,重新赋值新的 Entry
        if (k == null) {
            replaceStaleEntry(key, value, i); 
            return;
        }
    }

    // 如果该索引位置没有值,则直接赋值为新值
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}


// ThreadLocal 的 createMap() 方法
void createMap(Thread t, T firstValue) { // Thread 的 ThreadLocalMap 为 null 时,为 Thread 创建新的 ThreadLocal 并将 ThreadLocal 及 Value 存储
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

// ThreadLocalMap 的构造方法
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY]; // 初始化 Entry[] 
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // 根据 ThreadLocal 计算索引值
    table[i] = new Entry(firstKey, firstValue); 为该索引位置赋值
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

通过在 Thread 中获取 ThreadLocalMap ,再根据 TheadLocal 和 Value 将数据存储到了 Entry[] 数组中,具体的存储过程看注释。

有一个关键点,存储的时候,如果 ThreadLocal 对应的位置有值,则会查看向下一个位置,如果该位置还是有值则继续向下一位置,直到没有值的位置插入该 Entry

存:判断 ThreadLocalMap 是否为空
  • 为空:创建 ThreadLocalMap 并为对应位置赋值

  • 不为空:判断当前位置是否有值 Entry

    • 无值:为当前位置赋值为新的 Entry
    • 有值:判断 ThreadLocal 是否对应
      • 对应:修改旧 Value 值
      • 不对应:向下一有值索引位置判断,直到 ThreadLocal 对应(修改原 Value 值) 或遇到该索引对应值无 ThreadLocal 则为该位置赋值新 Entry,或遇到无值索引时为该位置赋值新 Entry

get() 方法源码解析

// ThreadLocal 的 get() 方法
public T get() {
    Thread t = Thread.currentThread();  // 获取当前线程 Thread
    ThreadLocalMap map = getMap(t); // 获取该线程中的 ThreadLocalMap
    if (map != null) { // 如果 ThreadLocalMap 不为空
        ThreadLocalMap.Entry e = map.getEntry(this); 调用 ThreadLocalMap 的 getEntry 方法获取 Entry 对象
        if (e != null) // Entry 不为空则将 Entry 中存储的 Value 返回
            return (T)e.value;
    }
    return setInitialValue(); // 线程的 ThreadLocalMap 为空或者 ThreadLocalMap 中无对应 Entry 情况
}

// ThreadLocalMap 的 getEntry() 方法
private Entry getEntry(ThreadLocal key) {
    int i = key.threadLocalHashCode & (table.length - 1); // 计算在 Entry[] 中的索引
    Entry e = table[i];
    if (e != null && e.get() == key) // 如果 Entry 不为 null 且当前位置对应的 ThreadLocal 为该 ThreadLocal 则返回 Entry
        return e;
    else 
        return getEntryAfterMiss(key, i, e); // 该位置不对应则向下一位置遍历查询
}

// ThreadLocalMap 的 getEntryAfterMiss() 方法
// Entry 为 null 或 ThreadLocal 不对应则向下一个位置遍历,直到 Entry 的 ThreadLocal 和当前 ThreadLocal 对应或遍历结束,如果遍历结束还是 null 则返回 null
private 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);
        else
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}


// ThreadLocalMap 的 setInitialValue() 方发法,线程的 ThreadLocalMap 为空或者 ThreadLocalMap 中无对应 Entry 情况
private T setInitialValue() {
    T value = initialValue(); // 该方法默认返回 null,可重新该方法修改默认值
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);  // ThreadLocalMap 不为空则为该位置创建值为默认值的 Entry
    else
        createMap(t, value); // ThreadLocalMap 为空时,则为该线程创建 ThreadLocalMap 并未该位置创建值为默认值的 Entry
    return value; // 将默认值返回
}

get() 方法主要为从 ThreadLocalMap 中取出对应的值,在该位置没有值或当前线程无对应 ThreadLocalMap 情况下,会返回默认的值 null

取,判断 ThreadLocalMap 是否为空
  • 为空

    • 创建新 ThreadLocalMap 并将该位置赋值 Value 为默认值的 新 Entry ,并返回
  • 不为空

    • 根据 ThreadLocal 计算索引,判断该索引位置是否有值
      • 无值:为该索引位置赋值 Value 为默认值的 新 Entry ,并返回
      • 有值:判断 ThreadLocal 是否匹配
        • 匹配:返回对应位置 Value
        • 不匹配:遍历下一位置直到 ThreadLocal 匹配时返回该 Entry,或该位置的 Entry 的 ThreadLocal 为空时删除该位置之后的所有空条目,继续向下一位置遍历,遍历结束如果还是没有对应 ThreadLocal 则返回 null
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值