`

ThreadLocal源码读后感总结

阅读更多

1.关联类

ThreadLocal: 线程局部变量

Thread:线程对象



2. Thread与ThreadLocal如何关联?

 ThreadLocal类:

 
   /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

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

 

 

 Thread类:

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

 

在每一个线程中都声明了一个成员变量ThreadLocalMap,而它的创建则是在ThreadLocal中通过createMap实现。第一次直接通过构造方法保存对象,之后取得ThreadLocalMap的引用进行set,保存在entry中。



3.一个ThreadLocal保存一个线程变量

 

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

 

 

threadLocalHashCode是ThreadLocal的唯一标识,ThreadLocalMap将ThreadLocal做为key值,因此同一个ThreadLocal对应的value将有且仅有一个,并只保存最后一次value值。起初脑袋被门夹了,一直想不明白ThreadLocal无论你set多少次只保存最后一个对象,定义ThreadLocalMap仅仅用来保存一个对象?后来才发现,原来是钻牛角尖了,当前线程下多new 几个ThreadLocal不就行了(学艺不精,思维没打开啊)。



4.ThreadLocalMap中Entry继承了WeakReference

虽然实现了弱引用,在内存不够的时候,即使还被引用,也能被GC回收,但是最好习惯性手动调用下

       /**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
 

 

如果你所应用的环境中存在线程池,当前线程没被销毁。下个应用可能使用上个线程,可能会造成上个线程的数据显示在下个线程中。所以在结束使用ThreadLocal时,记得手动调用下remove


5.ThreadLocalMap保存算法

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

 拿ThreadLocal的threadLocalHashCode与长度减一相与,求出哈希表的位置。如果这个插槽已有不为空的entry,则依次遍历之后的位置,当达到最大长度时,重新从第一个位置开始找,直到找着为null的插槽.增加size记录已有多少个元素,在查找过程中会判断这个entry弱引用是否已被回收,如果已回收则清楚value重新释放这个插糟并size-1,如果size等于加载因子则按2倍扩容。

 

 

6.总结

通过Thread.currentThread()取得当前线程,在这个线程中活动期间创建多个ThreadLocal放入当前线程的ThreadLocalMap中,相当于将多线程分开,每个人操作自己的线程,在自己线程中创建与使用自己的变量,各管各的,这就是所谓的“为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本”。

与synchronized相比只了解了其中之一的优点:“以空间换时间”。

ThreadLocal会随着线程的销毁而销毁

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics