ThreadLocal
一,前言
ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样子就可以解决在并发情况下,因为多个线程访问共享变量而引发的并发问题。
也可以用作当前线程的全局变量,例如 ,通过过滤器来获取请求的用户信息,然后作为一个全局变量set到ThreadLocal里,在其他地方调用get方法来取出。
二,常用的方法分析
以上是ThreadLocal的类结构图,ThreadLocalMap是底层真正存储的对象,ThreadLocalMap由一个Entry类型的数组,Entry对象类似map,只不过key是当前线程,value就是我们set进去的值。
我这里主要挑出几个常见的方法来分析:
set():设置值
get():获取值
remove():移除值
setInitialValue():设置初始值
getMap():返回一个ThreadLocalsMap对象
2.1 set()
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
拿当前线程作为key去获取ThreadLocalMap对象,如果为空,则去创建一个ThreadLocalMap,并且把当前的 value作为第一个值。否则,就往ThreadLocalMap里存值。
2.2 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();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
把当前线程作为key获取一个ThreadLocalMap对象,如果为空,就去设置一个初始值。在设置初始值的过程中,如果发现ThreadLocalMap也没有初始化(也就是map为null),就去创建一个value为null的ThreadLocalMap。如果ThreadLocalMap初始化了,那么就去把ThreadLocalMap的value设置成null,然后返回null。
2.3 remove()
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
通过当前线程拿到ThreadLocalMap对象,直接移除。
2.4 createMap()
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
创建一个ThreadLocalMap对象,key是传入的线程t,value是firstValue,在方法的调用者,Thread都是当前线程。
2.5 setInitialValue()
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
初始化值。如果ThreadLocalMap还未初始化,就去创建一个ThreadLocalMap且把它的值设置为null,否则,就把它的值设置成null。
initialValue()方法的返回值是一个null。
protected T initialValue() {
return null;
}
2.6 ThreadLocal代码小总结
观察上边的代码,可以看到,不管是set,get,还是remove,其实都是调用ThreadLocalMap来实现的。由此可以看到,ThreadLocalMap才是真正在干活的人。那么,我们就去看看ThreadLocalMap是个啥
2.7 ThreadLocalMap
以上是ThreadLocalMap的类结构,Entry是它存储的数据结构。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
这里就是一个类似Map的结构,key是线程,value是值。而且,Entry数组作为一个散列表,在ThreadLocalMap里使用线性探测法来确定下一个元素存储的位置。注意,它这里继承了一个弱引用!是可能会造成内存泄漏的原因,下边再分析。
这里也是主要分析ThreadLocalMap的set,get,remove方法
2.7.1 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();
}
拿到散列表table,通过线程id的哈希值计算出i,然后遍历散列表。
如果找到了对应的节点,则返回value
如果当前节点并不是要找的节点且当前节点为空,则触发清空方法,这也是为了防止内存泄漏。内存泄漏后续再讲解。
2.7.2 get()
待补充
2.7.3 remove()
待补充
本文地址:https://blog.csdn.net/A_droid/article/details/111500997