目录
什么是ThreadLocal?
ThreadLocal,即线程本地变量。一个共享变量存进该容器相当于在线程内部拷贝了一个副本。ThreadLocal里面的变量都是存在当前线程的。当操作ThreadLocal里面的变量时,实际操作的是存在自己线程的那个变量副本,该变量副本对于每一个线程都是独立的,从而实现了变量的隔离性,保证了线程安全。
ThreadLocal原理
在每一个Thread线程中都会拥有一个ThreadLocalMap类型的成员变量,这个变量初始值为null,
而ThreadLocalMap是ThreadLocal中的一个静态内部类
ThreadLocalMap的设计类似于HashMap,一样是通过Entry节点来存数据,一样是双边集合,不过该Entry与HashMap中的Entry不一样。前者是ThreadLocalMap的静态内部类,后者是Map的内部接口(实现类是Node)。另外,ThreadLocalMap的Entry内部类,key默认是当前threadLocal对象
static class ThreadLocalMap {
/**
ThreadLocalMap中的Entry继承了WeakReference(弱引用),entry中的key是使用super(K)进行赋值,说明 ThreadLocalMap的key对象是一个弱引用对象,而value是使用的=进行赋值,value就是一个强引用对象。
当对象被弱引用引用时,在下一次垃圾回收时,无论内存是否充足,都会被回收。
当对象被强引用引用时,即使在内存不足时,Java虚拟机也不会回收该对象,除非该对象被显式地设置为null。
当进行垃圾回收时,一个Thread线程中存储的ThreadLocal数据中的Key就会被回收,但是value不会,这样就导致一个本该被垃圾回收的对象却因为value还存在引用关系导致这个对象不能被彻底的回收,还占据着内存中的位置,这个就会引起内存泄漏问题。
所以对ThreadLocalLocal中存储的数据在不使用的时候就要通过remove方法将它移除
**/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//.......................其他内容.....................
}
当往ThreadLocal中存数据时,会调用ThreadLocal的set方法
有关ThreadLocal中的储存数据的方法:
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
//获取当前线程的 ThreadLocalMap,如果不存在则返回 null。
ThreadLocalMap map = getMap(t);
if (map != null) {
//如果map不为null,就调用ThreadLocalMap的set方法储存值
map.set(this, value);
} else {
//如果map为null,说明当前线程还没有初始化 ThreadLocalMap
createMap(t, value);
}
}
//返回当前Thread线程中的ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//根据ThreadLocalMap的构造方法初始化ThreadLocalMap对象,并赋值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap的有参构造函数
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);
}
首先,set方法获取当前线程对象
Thread.currentThread()
,然后通过getMap(t)
方法获取当前线程的 ThreadLocalMap,这个方法用于获取当前线程的 ThreadLocalMap,如果不存在则返回 null。如果当前线程的 ThreadLocalMap 不为 null,则调用
map.set(this, value)
方法将当前 ThreadLocal 对象(即调用 set 方法的对象)和要设置的值 value 存入 ThreadLocalMap 中。这样,当前线程就可以通过这个 ThreadLocal 对象获取到相应的值了。如果当前线程的 ThreadLocalMap 为 null,说明当前线程还没有初始化 ThreadLocalMap,那么调用
createMap(t, value)
方法来为当前线程创建一个新的 ThreadLocalMap,并将当前 ThreadLocal 对象和要设置的值 value 存入该 Map 中。
总体来说,这段代码的作用是为当前线程设置一个 ThreadLocal 对象的值,如果当前线程还没有相应的 ThreadLocalMap,则先创建一个。
当取数据时,一样拿到当前线程的ThreadLocalMap,并以当前threadLocal实例为key从里面拿出数据。
get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
首先,get方法获取当前线程对象
Thread.currentThread()
,然后通过getMap(t)
方法获取当前线程的ThreadLocalMap
,这个方法用于获取当前线程的ThreadLocalMap
,如果不存在则返回 null。如果当前线程的
ThreadLocalMap
不为 null,则调用map.getEntry(this)
方法从ThreadLocalMap
中获取与当前ThreadLocal
对象关联的ThreadLocalMap.Entry
对象。ThreadLocalMap.Entry
对象中包含了ThreadLocal
对象和对应的值。如果获取到的
Entry
对象不为 null,则将其值转换为泛型类型T
并返回。由于ThreadLocalMap.Entry
的值是 Object 类型,因此需要进行类型转换。如果没有获取到对应的
Entry
对象,或者当前线程没有ThreadLocalMap
,则调用setInitialValue()
方法来设置初始值并返回。
总体来说,这段代码的作用是获取当前线程的 ThreadLocal
对象所关联的值,如果该值尚未初始化,则调用 setInitialValue()
方法来设置初始值。
所以 ThreadLocal
本身并不存储值,它只是作为一个 key 来让Thread线程从它的 ThreadLocalMap
对象中获取 value。