美文网首页
ThreadLocal 详解

ThreadLocal 详解

作者: Cuccci | 来源:发表于2019-11-02 16:17 被阅读0次

    ThreadLocal 详解

    1. 前言

    ThreadLocal是java线程中的局部变量,变量作用域仅在当前线程有效。ThreadLocal常用于在多线程中,各自线程保存自己私有的变量值,如多数据源切换数据源。

    2. ThreadLocal源码详解

    ThreadLocal的主要3个方法:get、set、remove

        
        public T get() {
            // 获取当前所在线程
            Thread t = Thread.currentThread();
            // 获取当前线程对应的ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            // 获取ThreadLocalMap中对应的值
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            // ThreadLocalMap为null或者在对应的map中不存在对应的Key,则返回null,并构建一个key为this,value为null的map
            return setInitialValue();
        }
        
        
        private T setInitialValue() {
            // initialValue 返回的是 null
            T value = initialValue();
            // 查看当前线程是否存在ThreadLocalMap,存在直接set,不存在直接创建并set
            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) {
            // 查看当前线程是否存在ThreadLocalMap,存在直接set,不存在直接创建并set
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
        
        
        public void remove() {
            // 如果当前线程存在ThreadLocalMap存在,则移除对应的ThreadLocal
            ThreadLocalMap m = getMap(Thread.currentThread());   
            if (m != null)       
                m.remove(this);
        }
    
    
    
    

    从上面的代码分析,可以通过上面的代码分析出一个线程只有一个ThreadLocalMap,那么ThreadLocalMap是如何进行set、get?

    3. ThreadLocalMap 源码详解

            // ThreadLocalMap构造器,可以看出数据通过存放在Entry[]数组中
            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);
            }
            
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
    
                // 以ThreadLocal为Key
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
            
         private void set(ThreadLocal<?> key, Object value) {
                // ThreadLocalMap中的Entry[]数组
                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)]) {
                    ThreadLocal<?> k = e.get();
                    // 当key已经存在于Entry数组中的情况
                    if (k == key) {
                        e.value = value;
                        return;
                    }
                    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
                
                // 当遍历上面并没有成功创建set,则进行扩容
                tab[i] = new Entry(key, value);
                int sz = ++size;
                // 扩容方式
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
            
            
            // 以下为计算hashCode的方式,通过每次增加0x61c88647的方式
            
            private final int threadLocalHashCode = nextHashCode();
    
    
            private static AtomicInteger nextHashCode = new AtomicInteger();
    
        
            private static final int HASH_INCREMENT = 0x61c88647;
    
        
            private static int nextHashCode() {
                return nextHashCode.getAndAdd(HASH_INCREMENT);
            }
            
    

    为什么要每次增加0x61c88647?
    因为0x61c88647是斐波那契散列乘数,它的优点是通过它散列出来的结果会比较均匀,可以很大程度上避免hash冲突

    相关文章

      网友评论

          本文标题:ThreadLocal 详解

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