ThreadLocal就是所谓的线程本地变量,意思是只能在当前线程获取到的变量,与当前线程绑定在一块的一个变量。
那是怎么做到的呢,看下面的ThreadLocal.set(T value)方法:
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;
}
ThreadLocal.ThreadLocalMap threadLocals = null;
从上面代码可知,往ThreadLocal里set值时,会先获取当前线程,然后从当前线程对象里获取存放ThreadLocal数据的ThreadLocalMap,这个类型是定义在ThreadLocal内部的一个内部类,这个下面再讲。且从下面的代码可知,ThreadLocalMap是用ThreadLocal对象作为key的一个map:
private void set(ThreadLocal<?> key, Object value) {
...
...
...
}
这也就意味着,每new一个ThreadLocal对象,都只能存放一个值。
由于数据是存放在当前线程对象里面的一个map里,所以是和当前线程绑定的。
ThreadLocal的应用
由于ThreadLocal的特性,我们可以利用这个类来做一些巧妙的应用,比如:
- 存放数据库连接,这样一个线程内多次get的就是同一个数据库连接。
- 存放traceId,做调用追踪,日志打印等。
内存泄漏问题
上面是ThreadLocal的引用关系图。
ThreadLocalMap中的Entry中的key对ThreadLocal对象的引用是一个弱引用,如果ThreadLocal实例的引用也被置为了null,那么ThreadLocal对象就会被回收,但是Entry里的value却不会,只有当当前线程对象被回收的时候,这个value才会被回收,这样在这段时间内,就会造成内存泄漏。
如果线程仅仅是一次性使用,那么不会有什么大问题,但是在线程池中,线程会被复用,那么就可能造成大量的内存泄漏,最终导致内存溢出。所以如果是在线程池中使用ThreadLocal,使用完了之后,一定记得remove。