ThreadLocal 线程本地变量

发布于 2024-02-11 10:42:48 字数 10489 浏览 15 评论 0

ThreadLocal 示例

线程本地变量。

当创建一个 ThreadLocal 变量后,访问这个变量的每一个线程都拥有这个变量的一个本地副本。

示例

public class ThreadLocalDemo {
    static ThreadLocal<String> localVar = new ThreadLocal<>();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            localVar.set("Thread 1 local var");
            print("thread1");
            localVar.remove();
            System.out.println("thread1 after remove: " + localVar.get());
        });

        Thread thread2 = new Thread(() -> {
            localVar.set("Thread 2 local var");
            print("thread2");
            System.out.println("thread2 after remove: " + localVar.get());
        });
        thread1.start();
        thread2.start();
    }

    static void print(String str) {
        System.out.println(str + ":" + localVar.get());
    }
}
thread2:Thread 2 local var
thread1:Thread 1 local var
thread2 after remove: Thread 2 local var
thread1 after remove: null

也就是 thread1 的 localVar.remove() 并不会清除 thread2 的 localVar。验证了是线程本地的变量,互相独立。

ThreadLocal 源码解读

set()

先从 localVar.set() 方法入手,进入 java.lang.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);
    }

set 方法里面:

  1. 获取当前线程
  2. 将当前线程作为 key 来获取 ThreadLocalMap 类型的一个 map
  3. map 为 null 就新建,map 存在就直接 set 值

java.lang.ThreadLocal#getMap

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

getMap 方法本质上是获取当前线程的 threadLocals 变量,继续研究该变量。

java.lang.Thread#threadLocals

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

回到前面的 createMap(t, value) 方法: java.lang.ThreadLocal#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
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

本质上就是创建当前线程的 threadLocals 变量。

get()

java.lang.ThreadLocal#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();
    }

获取当前线程,获取 ThreadLocalMap 实例变量 map

  • map 非空,根据此线程局部变量的当前线程副本获取 entry,返回 entry 的值,该值是泛型。
  • map 为空,初始化。

注意 map.getEntry(this) 中的 this 表示的是一个 ThreadLocal 对象。


java.lang.ThreadLocal#setInitialValue

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

set 方法的变体,非常类似,不同点在于提供了 value 的初始值,为 null。

remove()

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

总结

每个线程内部都有一个 threadLocals 的变量,变量类型为 ThreadLocalMap,本质是 HashMap。

key 为 ThreadLocal 变量的 this 引用,value 为设置进去的值。

InheritableThreadLocal 源码解读

让子线程可以访问父线程中设置的本地变量。

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

InheritableThreadLocal 继承自 ThreadLocal,重写了三个方法。其中 createMap 和 getMap 方法保证了使用 inheritableThreadLocals 代替 threadLocals

对于该构造方法 childValue(T parentValue) ,需要先查看 Thread 的默认构造函数

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ...              
        Thread parent = currentThread();
        ...
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

在创建线程时,如果 inheritThreadLocals 不为 null,那么这句:

this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

猜测是将父线程的 inheritableThreadLocals 的信息传递给了子线程 this.inheritableThreadLocals


再看 java.lang.ThreadLocal#createInheritedMap

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

createInheritedMap()内部:使用父线程的 inheritableThreadLocals,直接构造一个 ThreadLocalMap 赋值给子线程的 inheritableThreadLocals。


继续研究 ThreadLocalMap() 构造函数内部细节

        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

关注这句: Object value = key.childValue(e.value); ,将父线程的 inheritableThreadLocals 复制到新的 ThreadLocalMap 对象中。


示例+总结

InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("main thread local value");

Thread thread = new Thread(() -> System.out.println("threadLocal.get():" + threadLocal.get()));
thread.start();

System.out.println("main: "+threadLocal.get());
main: main thread local value
threadLocal.get():main thread local value
  1. 在主线程 new 一个 InheritableThreadLocal 对象并设置值后,父线程的 inheritableThreadLocals 变量不再为 null。
  2. 在主线程中创建子线程时,构造函数会把父线程的 inheritableThreadLocals 变量复制一份保存到子线程的 inheritableThreadLocals 里面。
  3. 于是,子线程可以获取到父线程设置的 inheritableThreadLocals 信息。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

qq_E2Iff7

文章 0 评论 0

Archangel

文章 0 评论 0

freedog

文章 0 评论 0

Hunk

文章 0 评论 0

18819270189

文章 0 评论 0

wenkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文