美文网首页
源码阅读 - ThreadLocal

源码阅读 - ThreadLocal

作者: 烟小花飞花 | 来源:发表于2018-03-27 16:56 被阅读0次

    0.什么是ThreadLocal

    线程本地变量,线程间读写同一个ThreadLocal实例是线程隔离的。

    1.实现方式

    Thread类有一个threadLocals:ThreadLocalMap实例对象,每个thread操作自己的map以隔离。

    2.主要数据结构

    2.1 ThreadLocalMap

    1. 实质是一个table:Entry[]数组,由ThreadLocal实例的hash&(table.length-1)决定index

    为了高效求余,tablen.length只能是2的幂,所以扩容时只能继续是2的幂,实现中采用2倍扩容

    1. 哈希冲突的解决:线性预测法(linear probing)

    2.2 ThreadLocalMap.Entry

    1. extends WeakReference 然后添加变量value存储值。

    3.常用api

    3.1 构造函数

    懒初始化,构造函数什么也不做,只有在实际使用的时候如果没初始化才会初始化。

    // 节选set部分初始化代码
    
    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    /**
     * Construct a new map initially containing (firstKey, firstValue).
     * ThreadLocalMaps are constructed lazily, so we only create
     * one when we have at least one entry to put in it.
     */
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
    

    3.2 set方法

    1. 计算出hash值,如果不冲突,直接存入
    2. 如果冲突,向后探测,存入
    3. 如果存入后容量到达阈值(capacity*2/3),扩容

    3.3 get方法

    1. 算出hash值,如果当前entry的reference是当前的ThreadLocal实例,直接取出,否则向后探测

    3.4 remove方法

    清理指定位置

    3.5 扩容

    发生的时间:如果set完后size大于阈值,做一次全量清理,如果清理完后,size大于阈值的3/4,则进行扩容。
    扩容后会重新计算hash存入新的新的数组。

    /**
     * Double the capacity of the table.
     */
    private void resize() {
        Entry[] oldTab = table;
        int oldLen = oldTab.length;
        int newLen = oldLen * 2;
        Entry[] newTab = new Entry[newLen];
        int count = 0;
        for (int j = 0; j < oldLen; ++j) {
            Entry e = oldTab[j];
            if (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null; // Help the GC
                } else {
                    int h = k.threadLocalHashCode & (newLen - 1);
                    while (newTab[h] != null)
                        h = nextIndex(h, newLen);
                    newTab[h] = e;
                    count++;
                }
            }
        }
        setThreshold(newLen);
        size = count;
        table = newTab;
    }
    

    3.6 其他

    由于Entry是继承WeakReference类的,所以有可能存在GC导致索引失效的问题,在get,set,resize时都会进行一定程度的清理。
    在清理时如果当前位置被清理了,还会检查其后的位置,因为有可能是因为hash冲突存过去的,如果是这种情况,会将元素重新找到位置存储。

    4.有意思的地方

    4.1 位运算求余

    这种方法只适用于对2的幂求余

    4.2 魔数0x61c88647

    参考2中对魔数的原理进行了说明。

    4.3 android版本

    在android版本的实现略有不同

    1. reference作为一个实例变量
    2. 数组改为capacity*2大小,使用偶数位置存放索引,+1位置存放value
    3. 相应的,index取值要保证每次算出的均为偶数,所以hash计算的时候是魔数*2增加
    //java的实现
    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
    
    //android的实现
    /**
     * Internal hash. We deliberately don't bother with #hashCode().
     * Hashes must be even. This ensures that the result of
     * (hash & (table.length - 1)) points to a key and not a value.
     *
     * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
     * every other bucket) to help prevent clustering.
     */
    private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
    

    4.4 注意

    类的注释中有这么一句,通常ThreadLocal实例是private static 类型。

    {@code ThreadLocal} instances are typically private
     * static fields in classes that wish to associate state with a thread (e.g.,
     * a user ID or Transaction ID).
    

    5.参考

    1. ThreadLocal源码build 1.8.0_121-b13版本
    2. https://www.cnblogs.com/micrari/p/6790229.html
    3. https://www.cnblogs.com/dolphin0520/p/3920407.html

    相关文章

      网友评论

          本文标题:源码阅读 - ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/ejysfftx.html