美文网首页
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