ThreadLocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。
实现原理:
public class Threadlocal {
static ThreadLocal<Person> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tl.get());
}).start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
tl.set(new Person());
}).start();
}
static class Person{
String name = "zhangsan";
}
}
上述程序,创建了两个线程,线程1将Person对象存储进ThreadLocal对象,线程2通过ThreadLocal对象获取存进去的Person对象,但是线程2永远都获取不到Person对象。我们查看ThreadLocal对象的set()方法源码,首先获取当前线程,然后拿到当前线程的ThreadLocalMap,该map以ThreadLocal对象为key,set的值为value,所以某个线程存储在ThreadLocal对象中的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);
}
防止内存泄漏
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
通过上述源码可以发现,ThreadLocalMap里面存储的entry继承了WeakReference,所以该Entry的key,即ThreadLocal对象是一个虚引用,这里为什么要用虚引用呢?
如图所示,堆中的ThreadLocal对象一共有两个引用指向它,一个是强引用tl,另一个是虚引用ThreadLocalMap中的key。如果不用虚引用,当强引用tl消失时,还会存在另外一个强引用key,那么这个ThreadLocal将不会被虚拟机回收,会造成内存泄漏。但是,即使ThreadLocal对象被回收后,value依旧存储在ThreadLocalMap中,只是key为null,永远不能被访问,还是会造成内存泄漏。所以当ThreadLocal对象不再使用后,需要执行remove()方法。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null){
m.remove(this);
}
}