什么是ThreadLocal?(本文基于JDK1.7)
从字面意思看是"线程本地变量",其实它就是一种数据结构,有点像HashMap,key---value模式,只不过在ThreadLocal中,一个线程只能保存一个value值,并且线程之间保存的数据互不干扰。
public class Test { |
上例中,主线程main首先通过set()方法向ThreadLocal中设置value为car,然后get()获取其中的值,然后method()方法启动线程th,并获取Threadlocal中的值,由于线程th之前没有设置过value,所以获取的为null。
|
由源码可知,每个Thread类都有个ThreadLocalMap的数据结构,当执行set()方法时,ThreadLocalMap存在当前线程的threadLocals变量中,在set()方法中,是从线程的threadLocals变量获取。
所以,在线程1中set的值,在线程2中是看不到的,而且在线程2中set的话,也不影响到线程1中的值,保证了线程之间互不干扰。
每个线程中的ThreadLocalMap究竟是什么?
从名字上看貌似有点像HashMap,但是在源码中我们发现它并没有实现Map接口。在ThreadLocalMap中初始化了一个大小16的Entry数组,Entry对象用来保存一个键值对,这里的key是ThreadLocal对象。
set()方法是怎么解决冲突的呢?先看看set源码
private void set(ThreadLocal key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } |
3:位置i不为空并且和key不相同,则查找下一个位置。
同样的在get过程中,先根据ThreadLocal对象的hash值,定位到tab中的位置,判断该位置上的Entry对象的key和要查找的key是否一致,如果一致则返回对应的value,否则查找下一个。
内存泄漏
先来看看Entry的源码实现
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } |
如何避免内存泄漏呢?
在调用ThreadLocal的get和set方法时,可能会清楚key为Null的Entry对象,这样对于的value就没有GC Roots可达了,下次GC时就可以被回收,所以每次调用set方法之后请调用remove方法。