ThreadLocal 分析

2020-07-10  本文已影响0人  nuclear_sun

基本原理

每个线程对象保存了一个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

        /**
         * 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;
        }
        /**
         * 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 方法。

上一篇下一篇

猜你喜欢

热点阅读