zoukankan      html  css  js  c++  java
  • ThreadLocal源码解析

    作者:追梦1819
    原文:https://www.cnblogs.com/yanfei1819/p/14735212.html
    版权声明:本文为博主原创文章,转载请附上博文链接!


    Hash函数

    在哈希表(散列表)里,Hash函数的作用就是将关键字Key转化为一个固定长度数组的下标,以便存取键值对<Key,Value>,那当多个键(key)经过Hash函数处理后落在了同一个位置时怎么办呢?


    开放地址法

    所谓开放地址法就是发生冲突时在散列表(也就是数组里)里去寻找合适的位置存取对应的元素。

    这个合适的位置该怎么找呢?
    线性探测法
    当前位置冲突了,那我就去找相邻的下一个位置。
    就拿放入元素举例吧,当你放入<a,101>到下标为2的位置后,另一个<c,103>键值对也落入了这个位置,那么它就向后依次加一寻找合适的位置,然后把<c,103>放入进去。

    我们把这种方法称作线性探测法,我们可以将Hash以及寻找位置的过程抽象成一个函数:

    首先看(hash1(key)+0)%7 位置是自己最终的位置吗?如果有冲突,就探测(查看)下一个位置:(hash1(key)+1)%7。依次进行
    所谓探测,就是在插入的时候检查哪个位置可以插入,或者查找时查找哪个位置是要查找的键值对,本质就是探寻这个键值对最终的位置。
    但是这样会有一个问题,就是随着键值对的增多,会在哈希表里形成连续的键值对

    这样的话,当插入元素时,任意一个落入这个区间的元素都要一直探测到区间末尾,并且最终将自己加入到这个区间内。这样就会导致落在区间内的关键字Key要进行多次探测才能找到合适的位置,并且还会继续增大这个连续区间,使探测时间变得更长,这样的现象被称为“一次聚集(primary clustering”



    平方探测法

    我们可以在探测时不一个挨着一个地向后探测,我们可以跳跃着探测,这样就避免了一次聚集。
    其实我们可以让它按照 i^2 的规律来跳跃探测


    这样的话,元素就不会聚集在某一块区域了,我们把这种方法称为平方探测法
    同样我们可以抽象成下面的函数:

    虽然平方探测法解决了线性探测法的一次聚集,但是它也有一个小问题,就是关键字key散列到同一位置后探测时的路径是一样的。

    这样对于许多落在同一位置的关键字而言,越是后面插入的元素,探测的时间就越长。
    这种现象被称作“二次聚集(secondary clustering)”,其实这个在线性探测法里也有。
    这种现象出现的原因是由于对于落在同一个位置的关键字我们采取了一个依赖 i 的函数(i或者i^2)来进行探测,它不会因为关键字的不同或其他因素而改变探测的路径。


    又散列

    我们可以再弄另外一个Hash函数,对落在同一个位置的关键字进行再次的Hash,探测的时候就用依赖这个Hash值去探测,比如我们可以使用下面的函数:

    经过hash1的散列后,会定位到某一个地址,如果这个地址冲突,那么就按照1hash2(key)、2hash2(key)... 的偏移去探测合适的位置。

    由于Hash2函数不同于Hash1,所以两个不同的关键字Hash1值和Hash2值同时相同的概率就会变得非常低。
    这样就避免了二次聚集,但同时也付出了计算另一个散列函数Hash2的代价。(而且需要避免hash2(key) = 0的情况)


    ThreadLocal实现的原理

    ThreadLocal、ThreadLocalMap、Thread之间的关系

    ThreadLocal连接ThreadLocalMap与Thread,ThreadLocalMap是ThreadLocal内部类,由ThreadLocal创建,Thread类中有ThreadLocal.ThreadLocalMap类型的属性。


    ThreadLocalMap

    用于存储数据,采用类似HashMap的数据结构(并没有实现Map接口),存储了以threadLocal为key,需求隔离的数据为value的Entry键值对数组结构。


    源码解析

    package java.lang;
    import java.lang.ref.*;
    import java.util.Objects;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.function.Supplier;
    
    public class ThreadLocal<T> {
        
        // 根据hash值计算存放的Entry[]数组的下标
        private final int threadLocalHashCode = nextHashCode();
    
        private static AtomicInteger nextHashCode =
            new AtomicInteger();
    
        // 魔数:用于实现元素的完美散列
        private static final int HASH_INCREMENT = 0x61c88647;
    
        // 线程中每添加一个ThreadLocal,AtomicInteger类型的newHashCode值就会增加一个HASH_INCREMENT
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }
    
        // 初始值
        protected T initialValue() {
            return null;
        }
    
        public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
            return new SuppliedThreadLocal<>(supplier);
        }
    
        public ThreadLocal() {
        }
    
        public T get() {
            Thread t = Thread.currentThread();
            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();
        }
    
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
    
        public void set(T value) {
            Thread t = Thread.currentThread();
            // 从Thread类的成员变量中获取ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            // 只果ThreadLocalMap存在则将当前ThreadLocal作为key,要存储的值作为value存在ThreadLocalMap里面,否则新建一个ThreadLocalMap并赋值给Thread的变量threadLocals
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
         public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    
        static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }
    
        T childValue(T parentValue) {
            throw new UnsupportedOperationException();
        }
    
        static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
    
            private final Supplier<? extends T> supplier;
    
            SuppliedThreadLocal(Supplier<? extends T> supplier) {
                this.supplier = Objects.requireNonNull(supplier);
            }
    
            @Override
            protected T initialValue() {
                return supplier.get();
            }
        }
    
        static class ThreadLocalMap {
    
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
            // 数组初始化大小为16
            private static final int INITIAL_CAPACITY = 16;
    
            // 用来保存每一个key-value键值对,这里的key为ThreadLocal对象,调用ThreadlLocal对象的set方法时,把ThreadLocal对象自己当做key,放进了ThreadLocalMap中
            private Entry[] table;
    
            private int size = 0;
    
            private int threshold; // Default to 0
    
            private void setThreshold(int len) {
                threshold = len * 2 / 3;
            }
    
            private static int nextIndex(int i, int len) {
                return ((i + 1 < len) ? i + 1 : 0);
            }
    
            private static int prevIndex(int i, int len) {
                return ((i - 1 >= 0) ? i - 1 : len - 1);
            }
    
            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);
            }
    
            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++;
                        }
                    }
                }
            }
    
            private Entry getEntry(ThreadLocal<?> key) {
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
                if (e != null && e.get() == key)
                    return e;
                else
                    return getEntryAfterMiss(key, i, e);
            }
    
            private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
                Entry[] tab = table;
                int len = tab.length;
    
                while (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == key)
                        return e;
                    if (k == null)
                        expungeStaleEntry(i);
                    else
                        i = nextIndex(i, len);
                    e = tab[i];
                }
                return null;
            }
    
            private void set(ThreadLocal<?> key, Object value) {
    
                Entry[] tab = table;
                int len = tab.length;
                // 计算数组放置的位置,即数组下标,当len=2^n次幂时同 % n
                int i = key.threadLocalHashCode & (len-1);
              
                // 当数组位置的Entry不为null,则遍历i到Entry为null的位置
                for (Entry e = tab[i];
                     e != null;
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal<?> k = e.get();
                    
                    // Entry保存的key与调用的ThreadLocal相等,则替换value值并直接返回
                    if (k == key) {
                        e.value = value;
                        return;
                    }
                    
                    // 如果key为null(GC回收),用新的key、value覆盖,同时清理历史key=null的数据并直接返回
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
              
                // 如果i位置的Entry为null,则创建key、value的Entry
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    // 重新hash
                    rehash();
            }
    
            private void remove(ThreadLocal<?> key) {
                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)]) {
                    if (e.get() == key) {
                        e.clear();
                        expungeStaleEntry(i);
                        return;
                    }
                }
            }
    
            private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                           int staleSlot) {
                Entry[] tab = table;
                int len = tab.length;
                Entry e;
    
                int slotToExpunge = staleSlot;
                for (int i = prevIndex(staleSlot, len);
                     (e = tab[i]) != null;
                     i = prevIndex(i, len))
                    if (e.get() == null)
                        slotToExpunge = i;
    
                for (int i = nextIndex(staleSlot, len);
                     (e = tab[i]) != null;
                     i = nextIndex(i, len)) {
                    ThreadLocal<?> k = e.get();
    
                    if (k == key) {
                        e.value = value;
    
                        tab[i] = tab[staleSlot];
                        tab[staleSlot] = e;
    
                        // Start expunge at preceding stale entry if it exists
                        if (slotToExpunge == staleSlot)
                            slotToExpunge = i;
                        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                        return;
                    }
    
                    if (k == null && slotToExpunge == staleSlot)
                        slotToExpunge = i;
                }
    
                // If key not found, put new entry in stale slot
                tab[staleSlot].value = null;
                tab[staleSlot] = new Entry(key, value);
    
                // If there are any other stale entries in run, expunge them
                if (slotToExpunge != staleSlot)
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
            }
    
            private int expungeStaleEntry(int staleSlot) {
                Entry[] tab = table;
                int len = tab.length;
    
                // expunge entry at staleSlot
                tab[staleSlot].value = null;
                tab[staleSlot] = null;
                size--;
    
                // Rehash until we encounter null
                Entry e;
                int i;
                for (i = nextIndex(staleSlot, len);
                     (e = tab[i]) != null;
                     i = nextIndex(i, len)) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null;
                        tab[i] = null;
                        size--;
                    } else {
                        int h = k.threadLocalHashCode & (len - 1);
                        if (h != i) {
                            tab[i] = null;
    
                            // Unlike Knuth 6.4 Algorithm R, we must scan until
                            // null because multiple entries could have been stale.
                            while (tab[h] != null)
                                h = nextIndex(h, len);
                            tab[h] = e;
                        }
                    }
                }
                return i;
            }
    
            private boolean cleanSomeSlots(int i, int n) {
                boolean removed = false;
                Entry[] tab = table;
                int len = tab.length;
                do {
                    i = nextIndex(i, len);
                    Entry e = tab[i];
                    if (e != null && e.get() == null) {
                        n = len;
                        removed = true;
                        i = expungeStaleEntry(i);
                    }
                } while ( (n >>>= 1) != 0);
                return removed;
            }
    
            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;
            }
    
            private void expungeStaleEntries() {
                Entry[] tab = table;
                int len = tab.length;
                for (int j = 0; j < len; j++) {
                    Entry e = tab[j];
                    if (e != null && e.get() == null)
                        expungeStaleEntry(j);
                }
            }
        }
    }
    
    

  • 相关阅读:
    文件夹无法删除解决方案
    常用Web Service汇总(天气预报、时刻表等)
    浏览器兼容手册
    如何在word2007下右键添加“新建Word 2003 文档”
    Centos7上实现不同网段的服务器文件共享
    ubuntu安装界面 会出现不完整情况
    Centos7搭建dhcp服务器
    Centos7上搭建ftp服务器
    Centos7上配置网络和本地yum方法
    浅谈网络流
  • 原文地址:https://www.cnblogs.com/yanfei1819/p/14735212.html
Copyright © 2011-2022 走看看