文章目录
前言
可能很多小伙伴都听说过ThreadLocal,但是在具体面试的时候,却对底层原理以及如何使用感到困惑,我在写这篇文章的时候,和你们是一样的心情
面试官常问的问题:
1.ThreadLocal听说过吗?你知道他底层是怎么实现的吗?
2.知道ThreadLocal内存泄漏问题吗?怎么解决的?
一、ThreadLocal是什么?
ThreadLocal直译过来就是线程本地的意思,实际上ThreadLocal是线程本地变量,每个线程独有,且不与其他线程变量冲突,和普通变量不同,它是与当前线程关联,每个线程都可以使用其get或set方法来访问自己的独立初始化变量副本,通常是 private static final。这就实现了线程隔离
二、底层实现原理
1.ThreadLocalMap
ThreadLocal底层实现了ThreadLocalMap,ThreadLocalMap实现了内部类Entry,将线程与vale保存。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
2.ThreadLocal主要方法
2.1 set(T value)为线程绑定变量
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取该线程对象的ThreadLocalMap
if (map != null)
map.set(this, value);//如果map不为空,执行set操作,以当前threadLocal对象为key,实际存储对象为value进行set操作
else
createMap(t, value);//如果map为空,则为该线程创建ThreadLocalMap
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
从getMap与CreateMap可以看出ThreadLocal只不过是一个入口,而真正的变量副本是绑定在线程上的
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();//如果map为空,也就是第一次没有调用set直接get(或者调用过set,又调用了remove)时,为其设定初始值
}
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;
}
现在我们可以知道ThreadLocal底层实现逻辑:
- ThreadLocal只不过是绑定变量副本的一个入口
- 每一个线程都有一个ThreadLocalMap对象,这个ThreadLocalMap持有对对象的引用
- ThreadLocalMap以当前的threadLocal对象为key,以真正的存储对象为value。get()方法时通过threadLocal实例就可以找到绑定在当前线程上的副本对象
直接来看,就是类似于一个Map<Key,Value>的形式,一个线程对应一个存储对象
3.ThreadLocal的内存泄漏问题
我们看到ThreadLocal是底层是一个ThreadLocalMap,而它底层是由Entry实现的,而Entry是继承于WeakReference,这里可能有小伙伴还对强软弱虚引用不太了解,这里再加强一下
3.1强引用
如果一个对象具有强引用,那么垃圾回收器是不会回收他的,当内存空间不足,会报出OutOfMemoryError的错,是程序异常或终止,也不会通过回收强引用对象来解决问题
3.2软引用
如果内存空间充足,那么垃圾回收处理器是不会回收它的,在内存空间不足的情况下才会回收软引用对象,可以用来实现内存敏感的高速缓存
3.3弱引用
如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
3.4虚引用
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动
说完了强软弱虚引用,我们回到正题,为什么会出现内存泄漏呢,对于ThreadLocalMap中的Entry它的key为一个弱引用,而Value是一个强引用,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。
但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。
实现代表强引用,虚线代表弱引用
解决方式:通过自身的remove方法解决
/**
* Remove the entry for key.
*/
private void remove(ThreadLocal<?> key) {
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)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
4.ThreadLocal使用实例
public class ThreadLocalDemo {
public static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args) {
ThreadLocalDemo.threadLocal.set("hello world main");
System.out.println("创建新线程前,主线程" + Thread.currentThread().getName() + "的threadlocal字符值为:" + ThreadLocalDemo.threadLocal.get());
try {
Thread thread = new Thread() {
@Override
public void run() {
ThreadLocalDemo.threadLocal.set("new thread");
System.out.println("新线程" + Thread.currentThread().getName() + "的threadlocal字符值为:" + ThreadLocalDemo.threadLocal.get());
}
};
thread.start();
thread.join();
} catch (Exception e) {
System.out.println(e);
}
System.out.println("创建新线程后,主线程" + Thread.currentThread().getName() + "的threadlocal字符值为:" + ThreadLocalDemo.threadLocal.get());
}
}
总结
这篇ThreadLocal,你get到了那些点?我们在之后会遇到更多的问题,慢慢成长,才是最好的自己