关于ThreadLocal的几点误区
我们都知道遇到线程安全问题时可以用ThreadLocal来解决。但是关于这个类,很多人都存在一些误区。
误区一:ThreadLocal就是一个map,key就是线程相关的参数,value就是要存放的数据。
误区二:一个线程对应一个ThreadLocal,重复设置值会被覆盖。
先看一个例子:
public class ThreadLocalTest {
public static ThreadLocal<String> threadLocalOne = new ThreadLocal<String>();
public static ThreadLocal<String> threadLocalTwo = new ThreadLocal<String>();
public static void main(String[] args) {
threadLocalOne.set("first!");
threadLocalTwo.set("second!");
System.out.println(threadLocalOne.get());
System.out.println(threadLocalTwo.get());
}
}
运行结果:
first!
second!
说明一个线程其实可以设置多个与当前线程相关的数据,只是要设置多个ThreadLocal,否则如果只在一个ThreadLocal里面重复设置是会被覆盖的。
那么ThreadLocal是不是就仅仅是一个类似map的东西呢?
ThreadLocal的set方法只是把线程id相关的数据作为key放进了map吗?
我们一起来看看它的set方法吧,请看代码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
设置数据的时候其实是把ThreadLocal对象本身作为key放到了一个map里面,注意这里不是线程本身,这个map就是ThreadLocalMap类型的对象,这个ThreadLocalMap是哪里来的呢?
我们看看getMap(t)这个方法是怎么实现的吧:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
其实就是从当前线程对象里面的一个字段获取的。
threadLocals是Thread类型的对象里面的一个成员变量,我们在new Thread的时候并不会初始化这个成员变量,它的初始化在ThreadLocal的set方法里面。
createMap(t, value);这个代码就会去初始化这个成员变量:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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就非常类似我们的HashMap了。
总结起来就是:ThreadLocal的set方法其实就是把自己作为map的key,数据作为value设置到了当前线程对象的成员变量threadLocals里面,而这个threadLocals成员变量其实就是一个非常类似于HashMap的map类型对象,这个对象会在第一次set方法被调用时候初始化。也就是说如果要存放多个与线程相关的对象只需要多new几个ThreadLocal,每个ThreadLocal对应一个数据对象,每个数据对象的key就是各个ThreadLocal本身。