美文网首页
ThreadLocal

ThreadLocal

作者: 帮我的鸵鸟盖个章 | 来源:发表于2020-09-12 17:37 被阅读0次

    1. ThreadLocal定义

    ThreadLocal是线程变量,用于填充当前线程的变量,该变量与其他线程是隔离的。

    ThreadLocal使用场景

    • 线程间数据隔离
    • 进行事务操作,用于存储事务信息
    • 数据库连接,Session会话管理
    • 在进行对象跨层传递的时候,使用ThreadLocal可打破层次间的约束

    2. ThreadLocal提供的方法

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

    先获取当前线程 t,以当前线程作为keyThreadLoaclMap中查询该线程的ThreadLocalMap,如果map存在,则将当前线程作为key,要存储的对象作为value存进去,如果不存在,则创建一个,再保存。

    那么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 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;
                }
            }
            // 下面还有很多
        }
    

    ThreadLocalMapThreadLoacl中定义的一个类部类,里面定义了Entry,使用当前线程作为key,保存线程中的成员对象。

    get()

    方法源码:

        /**
         * Returns the value in the current thread's copy of this
         * thread-local variable.  If the variable has no value for the
         * current thread, it is first initialized to the value returned
         * by an invocation of the {@link #initialValue} method.
         *
         * @return the current thread's value of this thread-local
         */
        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();
        }
    

    获取当前线程 t,然后以 t 为key,从ThreadLocalMap里面取变量值。如果 值为 null,则自动填充一个。

    自动填充value :

        /**
         * 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;
        }
    
        /**
         * Returns the current thread's "initial value" for this
         * thread-local variable.  This method will be invoked the first
         * time a thread accesses the variable with the {@link #get}
         * method, unless the thread previously invoked the {@link #set}
         * method, in which case the {@code initialValue} method will not
         * be invoked for the thread.  Normally, this method is invoked at
         * most once per thread, but it may be invoked again in case of
         * subsequent invocations of {@link #remove} followed by {@link #get}.
         *
         * <p>This implementation simply returns {@code null}; if the
         * programmer desires thread-local variables to have an initial
         * value other than {@code null}, {@code ThreadLocal} must be
         * subclassed, and this method overridden.  Typically, an
         * anonymous inner class will be used.
         *
         * @return the initial value for this thread-local
         */
        protected T initialValue() {
            return null;
        }
    

    其实填充的就是 null。所以如果需要手动初始化的时候,如果需要默认值,则需重写initialValue()方法。

    remove()

    方法源码:

        /**
         * Removes the current thread's value for this thread-local
         * variable.  If this thread-local variable is subsequently
         * {@linkplain #get read} by the current thread, its value will be
         * reinitialized by invoking its {@link #initialValue} method,
         * unless its value is {@linkplain #set set} by the current thread
         * in the interim.  This may result in multiple invocations of the
         * {@code initialValue} method in the current thread.
         *
         * @since 1.5
         */
         public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    

    ThreadLocalMap中移除。

    内部的源码十分简单。

    ThreadLocal本身不存储值,每个ThreadLocal中维护一个ThreadLocalMap,以当前的ThreadLocal作为key来存储变量。

    ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收。

    重点:突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMapkey没了,但是value还在,这就造成了内存泄漏。

    解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

    所以还是不知道何时使用?该如何使用?举个例子,当我们使用切面的时候,就可以用啦。比如利用自定义注解记录日志,可以将注解的参数放到当前的ThreadLoacl中,记录日志的时候再取出。ThreadLocalMapvalueHashMap保存参数。

    参考:
    面试官:知道ThreadLocal嘛?谈谈你对它的理解?(基于jdk1.8)

    一文带你彻底吃透ThreadLocal!

    相关文章

      网友评论

          本文标题:ThreadLocal

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