ThreadLocal详解

来源:互联网 发布:商业机构域名 编辑:程序博客网 时间:2024/06/01 14:19

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