文章目录
前言
3月份在面试中总结的java基础多线程ThreadLocal面试题,在这里总结一下
提示:以下是本篇文章正文内容,下面案例可供参考
一、ThreadLocal是什么?
此类提供线程局部变量。 这些变量与普通变量不同,因为每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。 线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)
二、Thread,ThreadLocal,ThreadLocalMap这三者是什么关系
1.类之间的关系
然后堆栈里面的
这里解释一下,然后再代码展示
1.首先 ThreadLocalMap 是 Thread 的成员变量,这个属性值为 threadLocals
2.当我们那这个 threadLocal 去调用 get() 方法的时候,拿到当前线程,然后在用当前线程的 threadLocals 这个属性获取threadLocalMap
3.然后拿到这个 threadLocalMap 我们再去 hash 运算,获取这个 Entry ,其中 key 为当前 threadLocal 对象
代码如下(示例):
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取该线程下的 threadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 通过当前 threadLocal 对象当作 key 获取 Entry 对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 没有则初始化一个
return setInitialValue();
}
然后我们再来看threadLocalMap结构
static class ThreadLocalMap {
// key 为弱引用的 ThreadLocal
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// table 初始化的容量,2的倍数
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
//数组中的占用的大小
private int size = 0;
// 处罚 resize 的值
private int threshold; // Default to 0
.....
}
2.既然这个Entry的key为一个弱引用的threadLocal,那会有什么问题
关于这个弱应用,当发生gc的时候会将这个弱引用回收掉
当jvm发生gc时,我们的key变为null,相当与下回这个坑位一直被占用,而里面的value为强引用,导致value内存一直占用,产生内存泄漏的问题
3.那么作者是怎么解决这个问题的呢
其中,作者在get,set,和remove方法中频繁用了expungeStaleEntry该方法,当便利entry数组的过程中,如果entry不为null,但是key为null,则将 value 置为null,再将这个entry置为null,达到释放对象的目的
代码如下(示例):
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 将该槽位上的置为null
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// entry不为null ,key为null,都将引用去掉
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
//给当前entry另寻去处
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
总结
面试问的很多都是细节,有些看博客什么的其实是比较大致的,最好还是平时多看源代码,总结一下,思考一下作者为什么这么设计,这么设计有什么好处,那么又有什么缺陷,作者是如何解决的,笔者碰到的面试问题就这些,可能写的不是很详细,望海涵