ThreadLocal 线程本地变量的使用

ThreadLocal是什么

顾名思义,指的是线程私有的变量,同一个对象,在不同线程中调用相同方法拿到的数据相互独立,我们先代码实验一下效果。

测试代码

public class ThreadLocalTest {
    private static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
    public static void test() {
        stringThreadLocal.set("initial");
        long currentTid0 = Thread.currentThread().getId();
        Logger.i("main thread currentId0: " + currentTid0 + ", stringThreadLocal: " + stringThreadLocal.get());

        WorkerThread.getInstance().submit(()->{
            long currentTid = Thread.currentThread().getId();
            Logger.i("outer thread currentId: " + currentTid + ", stringThreadLocal: " + stringThreadLocal.get());
            stringThreadLocal.set("thread: " + currentTid);
            WorkerThread.getInstance().submit(()->{
                long currentTid1 = Thread.currentThread().getId();
                Logger.i("in new thread currentId1: " + currentTid1 + ", stringThreadLocal: " + stringThreadLocal.get());
                stringThreadLocal.set("thread: " + currentTid1);
            });
        });

输出为:

  debug: main thread currentId0: 1, stringThreadLocal: initial
  debug: outer thread currentId: 16, stringThreadLocal: null
  debug: in new thread currentId1: 18, stringThreadLocal: null

可以看出,不同线程拿到的数据是不一样的!!!

我们知道,java堆中的对象是同进程的线程共享的;那么这个ThreadLocal 能够实现线程私有,它是怎么实现的呢?

从源码中找答案

  • 先看ThreadLocal.java 中关于set 的实现
//ThreadLocal.java
public void set(T value) {
        Thread t = Thread.currentThread();
		//获取属于当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);//如果map未创建,则创建新的
        }
    }
	
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
	    /**
     * 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); 
		// 以当前ThreadLocal对象为参数传入构建ThreadLocalMap,赋值给线程的成员变量
    }
// 构造ThreadLocalMap对象
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue); //key继续传入Entry结构
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
		
  • 然后继续看Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);// 实际上,传入进来的ThreadLocal对象是以弱引用的方式被引用的,是为了避免内存泄漏
                value = v;
            }
}
  • 我们接着看get方法
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); //拿到当前线程的map
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); //拿到entry
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
// 我们再看下是怎么getEntry的
 private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.refersTo(key))// 看entry是否引用了key,这就和构造时对应起来了
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

总结

ThreadLocal 能够实现同一个对象,在不同的线程有相互独立的值是因为相关线程对象Thread中有自己的私有变量ThreadLocalMap,每个线程调用set/put 访问变量时,实际上会先找到当前线程的ThreadLocalMap,然后从里面取,所以能够做到线程间相互独立。

posted @   吾日三省吾身-学习  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示

目录导航