• ThreadLocal解析


    Java同一线程进行多Java实例对象调用中,可以使用TheadLoca进行的数据传递。传递的数据在每个线程内部独享。

    1、使用ThreadLocal。

    public static void main(String[] args) {
            ThreadLocal<String> threadLocal = new ThreadLocal<String>();
            ThreadLocal<Integer> threadInt = new ThreadLocal<Integer>();  
            System.out.println(threadLocal.get() + "--" + threadInt.get());
            threadLocal.set("first");
            threadInt.set(1);
            System.out.println(threadLocal.get() + "--" + threadInt.get());
            threadLocal.set("second");
            threadInt.set(2);
            
            System.out.println(threadLocal.get() + "--" + threadInt.get());
            System.out.println(threadLocal.get() + "--" + threadInt.get());
        }

     2、ThreadLocal类:

    初始化ThreadLoca类时,构造函数为空

     /**
         * ThreadLocals rely on per-thread linear-probe hash maps attached
         * to each thread (Thread.threadLocals and
         * inheritableThreadLocals).  The ThreadLocal objects act as keys,
         * searched via threadLocalHashCode.  This is a custom hash code
         * (useful only within ThreadLocalMaps) that eliminates collisions
         * in the common case where consecutively constructed ThreadLocals
         * are used by the same threads, while remaining well-behaved in
         * less common cases.
         */
        private final int threadLocalHashCode = nextHashCode();/**
         * Creates a thread local variable.
         */
        public ThreadLocal() {
        }

    其中变量threadLocalHashCode为不可变的final类型,表明该ThreadLocal实例中的该属性一直不能改变,该变量赋值语句为:

     /**
         * The next hash code to be given out. Updated atomically. Starts at
         * zero.
         */
        private static AtomicInteger nextHashCode =
            new AtomicInteger();
    
        /**
         * The difference between successively generated hash codes - turns
         * implicit sequential thread-local IDs into near-optimally spread
         * multiplicative hash values for power-of-two-sized tables.
         */
        private static final int HASH_INCREMENT = 0x61c88647;
    
        /**
         * Returns the next hash code.
         */
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }

    使用了静态的类AtomicInteger,提供一个原子性的相加操作,保证每个线程之间的调用不会冲突,静态AtomicInteger保证每个ThreadLoca的初始化都不会得到相同的值。其中添加量为:0x61c88647(为什么是这个值?)。

    ThreadLocal提供了可供调用的方法:

      public T get()

      public void set(T value)

      public void remove()

    先看set方法:

    /**
         * Sets the current thread's copy of this thread-local variable
         * to the specified value.  Most subclasses will have no need to
         * override this method, relying solely on the {@link #initialValue}
         * method to set the values of thread-locals.
         *
         * @param value the value to be stored in the current thread's copy of
         *        this thread-local.
         */
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }

    使用当前Thread当做Key,获取ThreadLocalMap。

    /**
         * Get the map associated with a ThreadLocal. Overridden in
         * InheritableThreadLocal.
         *
         * @param  t the current thread
         * @return the map
         */
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }

    最终返回的是Thread中的变量,threadLocals。

     /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;

    Thread类中的变量,threadLocals为每个线程私有的属性。同一个线程中的每一个ThreadLocal调用getMap(Thread t),都会得到相同的值, ThreadLocalMap:

    /**
         * ThreadLocalMap is a customized hash map suitable only for
         * maintaining thread local values. No operations are exported
         * outside of the ThreadLocal class. The class is package private to
         * allow declaration of fields in class Thread.  To help deal with
         * very large and long-lived usages, the hash table entries use
         * WeakReferences for keys. However, since reference queues are not
         * used, stale entries are guaranteed to be removed only when
         * the table starts running out of space.
         */
        static class ThreadLocalMap {
     /**
             * The initial capacity -- MUST be a power of two.
             */
            private static final int INITIAL_CAPACITY = 16;
    
            /**
             * The table, resized as necessary.
             * table.length MUST always be a power of two.
             */
            private Entry[] table;
    
            /**
             * The number of entries in the table.
             */
            private int size = 0;
    
            /**
             * The next size value at which to resize.
             */
            private int threshold; // Default to 0

    ThreadLocalMap看名称以为Map的子类,其实为一个普通类,内部有一个数组  Entry[] table;

    在ThreadLocal中的Set方法中,若Thread对象中的threadLocals为null( Thread对象中该属性默认为null),则调用createMap方法:

     /**
         * Create the map associated with a ThreadLocal. Overridden in
         * InheritableThreadLocal.
         *
         * @param t the current thread
         * @param firstValue value for the initial entry of the map
         * @param map the map to store.
         */
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }

    new 出一个ThreadLocalMap对象,赋值给Thread的threadLocals属性,看构造方法:

    /**
             * Construct a new map initially containing (firstKey, firstValue).
             * ThreadLocalMaps are constructed lazily, so we only create
             * one when we have at least one entry to put in it.
             */
            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);
            }

    构造时需要传入两个参数, ThreadLocal与 Object作为key-value,  table数组初始化为16个, threadLocalHashCode为传入的ThreadLocal前面讲过的不变值的属性, 该值与15进行与的操作,得到ThreadLocal对应数组的index,同时设定该数组中index为new 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;
                }
            }

     Entry继承了WeakReference, 使用一个ThreadLocal 的Weak引用,关于Java中的4种引用:

    a.强引用(strong reference): Object obj = new Object(); obj就为一个强引用,obj=null后, 该对象可能会被JVM回收

    b.软引用(SoftReference): 

      SoftReference<Object> softref = new SoftReference<Object>(obj);  

      obj = null;

      在内存不够用的时候,才会回收软引用的对象。

    c.弱引用(WeakReference): 

      WeakReference<Object> weakRef = new WeakReference<Object>(obj);
      obj = null;

      该new出来的对象没有强引用连接时,下一次GC时,就会回收该对象。

    d.虚引用(PhantomReference),它保存ReferenceQueue中的轨迹

    引自:http://www.cnblogs.com/mengdd/p/3298852.html

    此处使用弱引用,表明该ThreadLocal的Key转换成弱引用的对象。此处使用弱引用的好处不影响该:ThreadLocal k 的生命周期,若程序运行后,该ThreadLocal k连接的对象没有强引用后,该Entry中的Key很快会被回收掉,变为null,这样的话 (private Entry[] table;)中的该index的位置就可以被再次使用了。

    其中 setThreshold(),设定数组的阀值为  len * 2 / 3,  初始化时为10.

    ThreadLocalMap中的set方法:

    /**
             * Set the value associated with key.
             *
             * @param key the thread local object
             * @param value the value to be set
             */
            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();
            }

    从key.threadLocalHashCode计算得到的第i个元素

    若不为null,开始向后遍历:

      若Entry中的k为Key,并且value也相等,则不做操作,返回。

      若Entry中的key为null,表明该Entry可以进行下一步操作(replaceStaleEntry方法),可能是替换该Entry,也可能是查其它....

    若为null,

      new Entry,赋值到数组中,

    rehash方法中,判断是否扩容数据, *2

    get方法首先从ThreadLocalMap中查找,

    /**
             * 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);
            }

    若数组中的第i个不符合,则调用getEntryAfterMiss,向后遍历。

    参考:

    http://www.cnblogs.com/digdeep/p/4510875.html

    http://wangxinchun.iteye.com/blog/1884228

  • 相关阅读:
    菜根谭#245
    菜根谭#244
    菜根谭#243
    菜根谭#242
    菜根谭#241
    菜根谭#240
    菜根谭#239
    菜根谭#238
    菜根谭#237
    [转载]Linux 内核list_head 学习(一)
  • 原文地址:https://www.cnblogs.com/zyzl/p/4855887.html
Copyright © 2020-2023  润新知