前沿
可先参考之前写的 https://www.yuque.com/u1687194/hspkbf/uvaeep
概览
代码
源码
class Thread {
//内部持有ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals;
}
class ThreadLocal<T> {
public T get() {
//首先获取线程持有的
//ThreadLocalMap
ThreadLocalMap map = Thread.currentThread().threadLocals;
//在ThreadLocalMap中
//查找变量
Entry e = map.getEntry(this);
return e.value;
}
static class ThreadLocalMap {
//内部是数组而不是Map
Entry[] table;
//根据ThreadLocal查找Entry
Entry getEntry(ThreadLocal key) {
//省略查找逻辑
}
//Entry定义
static class Entry extends WeakReference<ThreadLocal> {
Object value;
}
}
}
使用demo
public class BaseTest {
ThreadLocal<Long> longLocal = new ThreadLocal<>();
ThreadLocal<String> stringLocal = new ThreadLocal<>();
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public long getLong() {
return longLocal.get();
}
public String getString() {
return stringLocal.get();
}
}
Entry的key为什么要使用弱引用
每个thread中都有一个map成员变量(类型是ThreadLocal.ThreadLocalMap
)。map中的key为一个个Threadlocal
实例。 这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向Threadlocal实例
。所以当把Threadlocal
实例置为null以后(或者说方法块执行完了之后,其内部的申明的new ThreadLocal
实例)理应被释放了。但由于当前线程还存活,作为GCRoot扫描来说,ThreadLocalMap也存活,里面的key又是引用了已经实际无效的被释放的Threadlocal
。
注意!假如每个key都强引用指向threadlocal,也就是上图虚线那里是个强引用,那么这个threadlocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。
依然出现的内存泄露问题
虽然上述的弱引用解决了key,也就是线程的ThreadLocal能及时被回收,但是value却依然存在内存泄漏的问题。
当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收.
map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露,
因为存在一条从current thread连接过来的强引用.
只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.