理解ThreadLocal内部存储结构

属性解释

    //这个是ThreadLocal对象的哈希值,通过调用nextHashCode()方法获取
    private final int threadLocalHashCode = nextHashCode();
    //存储哈希值的变量,属性是AtomicInteger原子类,是一个静态变量
    private static AtomicInteger nextHashCode = new AtomicInteger();
    // 每一次nextHashCode增加的数值大小,表示哈希值的增量
    // 每创建一个ThreadLocal对象,nextHashCode就会增长HASH_INCREMENT
    // HASH_INCREMENT是一个黄金分割数,哈希增量为这个值
    // 可以使Map中的数分布均匀
    private static final int HASH_INCREMENT = 0x61c88647;
    //获取hash值
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    //初始化一个起始value,,一般这个方法都是会重写的
    protected T initialValue() {
        return null;
    }

get()方法分析

public T get() {
    //Thread.currentThread()是一个本地静态的native方法,返回一个Thread对象 
    Thread t = Thread.currentThread();
    //获取当前线程内部的ThreadLocalMap,一开始默认为null,直接走return的setInitialValue()
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
//初始化一个ThreadLocalMap,放进去一个null值
private T setInitialValue() {
    T value = initialValue();//返回一个null值
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        //返回的map 是null,则创建一个map放进去一个null值
        createMap(t, value);
    return value;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

set(T value)方法分析

//参考上面的setInitialValue方法,逻辑基本一样
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源码分析

成员变量

//ThreadLocalMap内部是一个Entry数组,INITIAL_CAPACITY就是这个Entry数组的容量大小
private static final int INITIAL_CAPACITY = 16;

//用来存放数据的entry数组
private Entry[] table;

//当前数组的大小
private int size = 0;

//扩容阈值,初始值为 len * 2 / 3
private int threshold; // Default to 0

private void setThreshold(int len) {
     threshold = len * 2 / 3;
}

构造方法

线程的ThreadLocalMap是延迟初始化的,只有当线程第一次存储key-value键值对时才会进行初始化。

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    //创建一个初始大小为16的entry数组
    table = new Entry[INITIAL_CAPACITY];
    //计算第一个值的hashcode
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //将值放入table中
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    //初始化阈值
    setThreshold(INITIAL_CAPACITY);
}

这里不小心看错了,下面这个方法不是ThreadLocalMap构造方法,这个是用来父子线程传递数据用的方法

private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (int j = 0; j < len; j++) {
        Entry e = parentTable[j];
        if (e != null) {
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                Object value = key.childValue(e.value);
                Entry c = new Entry(key, value);
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            }
        }
    }
}

202204241311119.png

Entry方法分析

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

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

引申出来的几个重要知识点

  • Java的四种对象引用级别

  • 为什么ThreadLocal设计成弱引用

    参考:谈谈ThreadLocal为什么被设计为弱引用 - 知乎 (zhihu.com)

  • 什么是对象可达,对象不可达

    简单点就是对象为null就是不可达

  • gc垃圾回收,逐步接触(需要系统的学习)

  • 堆栈数据怎么分析,使用什么工具,参数的含义

标签: none

仅有一条评论

  1. 测试头像

添加新评论