目录
什么是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。