ThreadLocal 源码分析

75 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

类定义

首先看ThreadLocal 的类定义上,是带有泛型标注的,说明ThreadLocal 可以存储任意类的数据。

public class ThreadLocal<T> {}

ThreadLocal的属性:

private final int threadLocalHashCode = nextHashCode();

private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

private static AtomicInteger nextHashCode = new AtomicInteger();


static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private static final int INITIAL_CAPACITY = 16;

        private Entry[] table;

        private int threshold;
}

......

  • threadLocalHashCode 表示当前 ThreadLocal 的 hashCode,主要用于计算当前 ThreadLocal对象 在 ThreadLocalMap 中的索引位置

  • nextHashCode() 主要是用于计算 ThreadLocal对象 的 hashCode 值

  • nextHashCode 属性使用了static修饰,并且数据类型是AtomicInteger,首先AtomicInteger保证了每个ThreadLocal的threadLocalHashCode 是唯一的,当然这必须得在不同JVM中;其次static可以使得在同一时刻,多个ThreadLocalMap被set到ThreadLocal时,需要使用到threadLocalHashCode进行唯一区分

  • ThreadLocalMap中的Entry表示数组中的每个节点值,继承了WeakReference,表示当前对象在没有引用指向时,进行JVM垃圾回收时就会被回收掉

  • ThreadLocalMap 的数组的初始化大小为16,扩容的初始阈值是数组大小的三分之二

ThreadLocalMap的set方法

        private void set(ThreadLocal<?> key, Object value) {
            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)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

int i = key.threadLocalHashCode & (len-1); 表示计算 key 在数组中的下标,其实就是 ThreadLocal 的 hashCode 和数组大小-1取余

for循环数组进行处理时 tab[i],首先查看 i 索引位置有没有值,有值的话,索引位置 + 1,直到找到没有值的位置

e = tab[i = nextIndex(i, len)])... nextIndex 就是让在不超过数组长度的基础上,把数组的索引位置 + 1

if (k == key) { e.value = value; return; } 找到内存地址一样的 ThreadLocal,直接替换

if (k == null) { ... } 判断当前 key 是否是 null,是则说明 ThreadLocal 被清理了,直接替换掉

set方法中用到了AtomicInteger类,Atomic相关的类主要用于线程不安全的情况下,做原子级别的递增,因为本身的递增操作是读取原值,+1后重新赋值,在线程不安全的情况下极有可能会发生递增值与预期不匹配。