美文网首页
ThreadLocal 分析

ThreadLocal 分析

作者: nuclear_sun | 来源:发表于2020-07-10 11:32 被阅读0次

    基本原理

    每个线程对象保存了一个map (Thread.threadLocals), 将线程本地对象放在这个 map 中(<ThreadLocal, T>)。

    使用样板

    class A {
    
        private ThreadLocal<Integer> count = new ThreadLocal<>(){
            protected Integer initialValue() {
                return Integer.valueOf(0);
            }
        }
    
        // 可能被多个线程访问
        public void threadSensitiveIncr(int n) {
           count.set(count.get() + n);
        }
    }
    
    

    事实上,上面的 count是多线程共享的对象,但是其封装的内容是线程私有的。

    关键行为分析

    获取 get

    • 注意,调用 ThreadLocal.get() 时可能还没创建封装的值,需要先创建,后续的调用才是现成的。
            /**
             * Get the entry associated with key.  This method
             * itself handles only the fast path: a direct hit of existing
             * key. It otherwise relays to getEntryAfterMiss.  This is
             * designed to maximize performance for direct hits, in part
             * by making this method readily inlinable.
             *
             * @param  key the thread local object
             * @return the entry associated with key, or null if no such
             */
            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);
            }
    
            /**
             * Version of getEntry method for use when key is not found in
             * its direct hash slot.
             *
             * @param  key the thread local object
             * @param  i the table index for key's hash code
             * @param  e the entry at table[i]
             * @return the entry associated with key, or null if no such
             */
            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;
            }
    
    • 每个线程持有的 map 是一个特制的 ThreadLocalMap,使用线性探测法实现的 Map, 且每个位置存放的是这样的 Entry:
            /**
             * The entries in this hash map extend WeakReference, using
             * its main ref field as the key (which is always a
             * ThreadLocal object).  Note that null keys (i.e. entry.get()
             * == null) mean that the key is no longer referenced, so the
             * entry can be expunged from table.  Such entries are referred to
             * as "stale entries" in the code that follows.
             */
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    

    也就是 key 被节点弱引用着,value 被强引用着。一段时间后可能存在数组中某个节点 key == null && value != null 的情况。

    在查找一个 key 的过程中,如果发现这种悬空节点(staled entry),会清除掉该节点,并且对数组中该位置,到下一个 null 的位置之间的所有节点,重新哈希。

    存在问题

    一些条件下,悬空节点越来越多,得不到清理,如,创建大量 ThreadLocal 对象,且每个对象只调用 set 方法。

    相关文章

      网友评论

          本文标题:ThreadLocal 分析

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