zoukankan      html  css  js  c++  java
  • ThreadLocal 源码解读

    一、引入

    public class Thread implements Runnable {
        /* 前面略  */
    
        /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;
        /* 后面略  */
     }
    

    首先我们看到的是 Thread 中有一个属性 threadLocals,它的类型是 ThreadLocalMap,封装类型是 default(表示它只能在包内可见),jdk 是这么介绍它的:与此线程有关的 ThreadLocal 值,该映射由 ThreadLocal 类维护。 啥意思呢?那就来看看 ThreadLocalMap 是啥玩意!

    public class ThreadLocal<T> {
       /* 前面略  */
    
        static class ThreadLocalMap {
    
            /**
             * 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;
                }
            }
       /* 后面略  */
       }
    }
    

    从类定义上可以看出 ThreadLocal 是支持泛型的,而 ThreadLocalMap 是 ThreadLocal 的一个内部类,封装类型也是 default(表示只能在包内可见),jdk 是这么介绍它的:ThreadLocalMap 是自定义的哈希映射,仅适用于维护线程局部值。并且为了存储容量可控,不至于内存泄漏,哈希表条目使用弱引用作为键(弱引用的对象的生命周期直到下一次垃圾回收之前被回收),ThreadLocalMap 使用静态内部类 Entry(可以类比 Map 中的 entry)来存储实际的 key 和 value。

    从上面这些介绍,我们可以大致想到,ThreadLocal 是一个与线程相关的类,用来存储维护线程局部值。

    二、set(T value) 方法解读

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

    可以看到 set(T value) 方法做的事情很简单,就是维护 Thread 的 threadLocals 属性,如果该属性不存在的话,就以当前 ThreadLocal 实例为 key 创建一个;该属性存在的话,则直接赋值。

    三、get() 方法解读

        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
        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;
        }
    
        protected T initialValue() {
            return null;
        }
    

    get() 的方法同样很简单,就是从 Thread 的 threadLocals 属性获取值,如果获取不到,则把 initialValue() 的值赋值给线程的 threadLocals 属性并返回。initialValue() 方法是一个 protected 类型的方法,默认返回 null,我们可以在创建 ThreadLocal 的时候重写它,表示所有线程的默认值。

        // java8 的方式
        ThreadLocal<Boolean> threadLocal1 = ThreadLocal.withInitial(() -> false);
        // 
        ThreadLocal<Boolean> threadLocal2 = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return false;
            }
        };
    

    四、总结

    • ThreadLocal 用于存储维护线程的局部值。
    • 和 ThreadLocal 类似的还有一个叫 InheritableThreadLocal, InheritableThreadLocal 继承自 ThreadLocal,用于父子线程间共享共同的值,父线程中设置的值,子线程中可以访问到。
    • 上面看 ThreadLocalMap 的时候,我们知道 key 是弱引用,gc 的时候 key 会被回收,但是 value 和 ThreadLocalMap 的引用不会被回收,如果这种情况的 Thread 很多,而且一直没有执行完,就可能会出现内存泄漏,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。
    • 当使用线程池的时候,在调用 ThreadLocal 的 set 方法后,却没有调用 remove 的方法,如果同一个线程再去调用 get 方法可能拿到的值并不是当时 set 进去的(因为线程池的线程是复用的),可能导致程序数据异常之类的,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。
  • 相关阅读:
    在C语言中,double、long、unsigned、int、char类型数据所占字节数
    C++基础之头文件和源文件的关系
    Activity与Fragment数据传递之Activity从Fragment获取数据 分类: Android 2015-07-02 09:56 12人阅读 评论(0) 收藏
    Activity与Fragment数据传递之Fragment从Activity获取数据 分类: Android 2015-07-01 14:12 17人阅读 评论(0) 收藏
    Java反射机制和对象序列化 分类: Java 2015-06-26 12:08 21人阅读 评论(0) 收藏
    Android通过播放多张图片形成一个动画 分类: Android 2015-04-24 14:05 16人阅读 评论(0) 收藏
    jvm参数的配置、垃圾回收器的配置
    selenium2工作原理
    LeetCode#1 Two Sum
    LeetCode#27 Remove Element
  • 原文地址:https://www.cnblogs.com/jmcui/p/11994720.html
Copyright © 2011-2022 走看看