美文网首页
ThreadLocal源码分析

ThreadLocal源码分析

作者: shoulda | 来源:发表于2018-07-02 20:49 被阅读0次

    1.对ThreadLocal的理解

    ThreadLocal是一个创建线程局部量的类。使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

    2.主要方法的分析

    2.1 set()

    //set值
    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
    //getMap()函数
    ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
    

    set的过程可以分为3个过程:
    1.获取当前的线程
    2.获取线程里面的ThreadLocal.ThreadLocalMap
    3.看这个ThreadLocal.ThreadLocalMap是否存在,存在就设置一个值,不存在就给线程创建一个ThreadLocal.ThreaLocalMap
    第三点有两个分支,如下:

    void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    //
    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 final int threadLocalHashCode = nextHashCode();
    //
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT); 
        }
    //
    private static AtomicInteger nextHashCode = 
        new AtomicInteger();
    

    这个Map中并没有next节点,所以他的存储方式不是链表的形式。
    下面看一下其创建分支:

    private void set(ThreadLocal key, Object value) {
    
                // We don't use a fast path as with get() because it is at
                // least as common to use set() to create new entries as
                // it is to replace existing ones, in which case, a fast
                // path would fail more often than not.
    
                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();
    
                    if (k == key) {
                        e.value = value;
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    private static int nextIndex(int i, int len) {
                return ((i + 1 < len) ? i + 1 : 0);
            }
    

    上面代码的大致逻辑可以分为:
    1.先对ThreadLocal里面的threadLocalHashCode取模获取到一个table的位置。
    2.如果这个位置有数据,获取这个位置上的ThreaLocal
    (1)判断一下位置上的ThreadLocal和本身这个是不是一个ThreadLocal,是的话数据就覆盖。
    (2)如果不是同一个ThreadLocal,就判断一下这个位置上的Threadlocal是否为空,返回。

    2.2 get()

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();
        }
    //获取Entry
    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;
            }
    

    概括一下上面的逻辑:
    1.获取当前线程
    2.去当前线程中得到ThreadLocal.ThreadLocalMap
    3.当前线程中判断是否有ThreadLocal.ThreadLocalMap
    (1)有就尝试根据当前ThreadLocal的ThreadLocalHashCode取模取table的值,有就返回,没有就+1继续找。
    (2)没有就调用set方法给当前线程ThreadLocal.ThreadLocalMap设置一个初始值。

    2.3remove()

    public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    

    取得当前线程的ThreadLocal.ThreadLocalMap,如果有ThreadLocal.ThreadLocalMap,找到对应的Entry,移除就可以了。

    参考:http://www.cnblogs.com/xrq730/p/4854813.html

    相关文章

      网友评论

          本文标题:ThreadLocal源码分析

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