ThreadLocal详解

本文深入解析了ThreadLocal的工作原理及其实现机制。介绍了ThreadLocal如何为每个线程提供独立的变量副本,避免了线程间的数据冲突问题。通过源码分析,详细说明了ThreadLocalMap的内部结构以及数据是如何存储和检索的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ThreadLocal类的作用是为每个线程都创建一个变量副本, 每个线程都可以修改自己所拥有的变量副本, 而不会影响其他线程的副本. 其实这也是解决线程安全的问题的一种方法.
最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。

1.ThreadLocal是怎么实现了多个线程之间每个线程一个变量副本的?
2.每个线程的变量副本是存储在哪里的?
3.ThreadLocalMap内部又是如何存储的?

通过查看ThreadLocal的构造设计就可以知道以上问题:
1.ThreadLocalMap是ThreadLocal的一个静态内部类;但ThreadLocal并不拥有ThreadLocalMap实例,只是定义以及代理操作;
2.Thread拥有ThreadLocalMap实例 threadLocals,即ThreadLocal中存放的对象实际上都存放在Thread中;
3.我们在每个Thread中,通过ThreadLocal来操作ThreadLocalMap,来存储我们的对象;
4.ThreadLocalMap底层通过数组来存放所有对象,具体每个对象的数组下标需要通过每个ThreadLocal的 threadLocalHashCode来计算获取;ThreadLocal提高了一个索引的作用。

下面通过源码来看看ThreadLocal的设计构造:
Thread拥有ThreadLocalMap实例 threadLocals;

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

ThreadLocal的set/get方法:

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

ThreadLocal提供了set和get访问器用来访问与当前线程相关联的线程局部变量。
可以从ThreadLocal的get函数中看出来,其中getmap函数是用t作为参数,这里t就是当前执行的线程。get函数就是从当前线程的threadlocalmap中取出当前线程对应的变量的副本【注意,变量是保存在线程中的,而不是保存在ThreadLocal变量中】。当前线程中,有一个变量引用名字是threadLocals,这个引用是在ThreadLocal类中createmap函数内初始化的。每个线程都有一个这样的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal对象声明的变量类型作为参数。这样,我们所使用的ThreadLocal变量的实际数据,通过get函数取值的时候,就是通过取出Thread中threadLocals引用的map,然后从这个map中根据当前threadLocal作为参数,取出数据。
在ThreadLocal的set函数中,可以看到,其中的map.set(this, value);把当前的threadlocal传入到map中作为键,也就是说,在不同的线程的threadlocals变量中,都会有一个以你所声明的那个线程局部变量threadlocal作为键的key-value。假设说声明了N个这样的线程局部变量变量,那么在线程的ThreadLocalMap中就会有n个分别以你的线程局部变量作为key的键值对。

ThreadLocalMap内部是一个类似散列表的东西,key(或者说索引更合适)是通过ThreadLocal实例的threadLocalHashCode算出来的,value是具体的内容实例,实际上ThreadLocal实例本身并不存储内容,只是充当了一个索引的作用。

/**
 * Construct a new map initially containing (firstKey, firstValue).
 * ThreadLocalMaps are constructed lazily, so we only create
 * one when we have at least one entry to put in it.
 */
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

总结:
线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。

参考:http://www.cnblogs.com/dolphin0520/p/3920407.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值