以下是Thread类的部分全局变量
//普通的线程本地变量表(key:ThreadLocal,value:需要保存的变量),只支持在当前线程上下文中获取set过的值
ThreadLocal.ThreadLocalMap threadLocals = null;
//可以在父子线程中传递值的线程本地变量(key:InheritableThreadLocal,value:需要保存的变量),其原理会在后面解释
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Thread -> ThreadLocal -> ThreadLocalMap
每个线程都会有一个本地变量表
,这个表就是ThreadLocalMap
,ThreadLocalMap
是ThreadLocal
的静态内部类,会在初始化ThreadLocal
完成,并且set值的时候初始化,获取在get的时候初始化(此时会返回一个null值)
以下是
ThreadLocal
调用set方法的代码块
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//拿到当前的全局变量 ThreadLocal.ThreadLocalMap threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
//设置值,取当前ThreadLocal的 hashcode % table.size()
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
//取当前ThreadLocal的 hashcode % Entry数组的大小
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) {
//******注意:这里直接覆盖原始值,不会解决hash冲突******
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();
}
以下是
ThreadLocal
调用get方法的代码块
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//取当前ThreadLocal的 hashcode % table.size(),得到Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//和set方法逻辑类似,只是多了一个initialValue操作
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;
}
//
protected T initialValue() {
return null;
}
ThreaLocal
—— InheritableThreadLocal
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
//实现父类的抽象方法
protected T childValue(T parentValue) {
return parentValue;
}
//重写父类的方法
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
//重写父类的方法
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
new Thread(() -> {}).start();
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
//****** 注意inheritThreadLocals变量,默认为true -> 默认需要初始化inheritableThreadLocals变量(即可以在父子线程传递变量的ThreadLocal)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// **** 检查父线程inheritableThreadLocals(ThreadLocalMap)是否为空 ****
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
//拿到父线程的Entry数组(实际存储value的地方)
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
//初始化和父线程一样大小的Entry数组
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
//找出不为null的Entry
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
//此时key为InheritableThreadLocal,实现了ThreadLocal中定义的抽象方法 childValue
Object value = key.childValue(e.value);
//封装一个父类有相同key 和 value Entry
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
//一直从下标 h 开始寻找第一个空的节点,用这种方法解决hash冲突
h = nextIndex(h, len);
//赋值到自己(子线程)变量表中
table[h] = c;
size++;
}
}
}
}
每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本。这些对象之间的引用关系如下:
什么时候出现内存泄漏
从上图中可以看出,hreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。获取线程存活时间很长,导致ThreadLocalMap熬过yangGC进入老年代,容易引起fullGC。
为什么ThreadLocalMap的key使用弱引用的ThreaLocal,不使用强引用
如果使用强引用的话,如果当前线程不结束,ThreadLocal会一直被ThreadLocalMap引用,导致内存泄漏
相反使用弱引用的话,如果ThreadLocal没有强引用指向它,那么不管线程有没有结束,会在下次GC的时候回收ThreadLocal对象。
当key为null,在下一次ThreadLocalMap调用set(),get(),remove()方法的时候会被清除value值。