美文网首页
ThreadLocalMap原理、详解

ThreadLocalMap原理、详解

作者: younger_lei | 来源:发表于2021-12-14 21:00 被阅读0次

基于JDK1.8

使用场景

1、在当前Thread中存取Looper
2、储存本线程特有的数据

基本原理

1、在Thread.java中设置了内部变量mThreadLocal,从而实现了数据同线程的绑定
2、ThreadLocalMap本质是哈希表,内部用Entry[]进行存取,储存节点是Entry(ThreadLocal key, Object value),通过key的哈希结合数组长度计算存取位置,其中key的类型是ThreadLocal。

    //数组存取数据位置
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

知识点

1、如何确定储存位置
2、如何扩容,阈值是多少
3、如何解决hash冲突
4、调用set()后内部如何执行
5、调用get()后内部如何执行

源码分析

1、threadLocalMap中同hashMap一样,大小设为了2的幂次,默认16,这样可以用“&”运算代替“%”,计算位置如下

    //数组存取数据位置  firstKey是ThreadLocal类型
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

上面threadLocalHashCode是当前ThreadLocal的hashCode,但它不是对当前threadLocal直接计算hash,而是直接增加一个固定值(Int值上限*黄金分割比例)

    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =  new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

2、阈值为当前数组长度的2/3 * 3/4 = 1/2。扩容时双倍扩容,首先遍历旧数组,如果key为null直接设置value为null,否则重新计算存储位置;如果此位置为null则直接赋值,否则使用线性探索法依次往后寻找空位置存储(线性探测法)。

       /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        private void rehash() {
            expungeStaleEntries();

            // Use lower threshold for doubling to avoid hysteresis
            if (size >= threshold - threshold / 4)
                resize();
        }

       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、未完待续,有空就写写

2021年11月 younger

相关文章

网友评论

      本文标题:ThreadLocalMap原理、详解

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