关于Threadlocal的理解
threadlocal
在使用threadlocal的时候其实不是threadlocal绑定线程,而是threadlocal被绑定在线程上,
而且可以有多个threadlocal被绑定在线程上,确切的说是作为key绑定在线程的hash表上!
线程是主导,threadlocal是被绑定
一个线程可以绑定多个threadlocal对象吗?
一个线程可以和很多threadlocal对象建立间接联系,直接和线程联系的是threadlocalMap对象,threadlocalMap 对象和具体的threadlocal对象没有关系(仅仅是他的内部类而已)
thread类中的这个map是以一个个threadlocal对象为key值,以用户的变量为value
所以只要在当前线程作用下创建多个threadlocal实例,那thread对象中的map就会有多个记录,每一个threadlocal对象为key
总结: 一个线程可以绑定多个threadlocal对象,线程真正绑定的是threadlocalMap,map里面会存储很多threadlocal和变量值的key-value
threadlocal的静态方法withInitial()和实例方法set()的异同点
俩个方法都能赋值
withInitial()是个懒加载方法,set()是直接加载
具体体现在set方法会直接判断当前线程的threadlocalMap是否为空,为空则创建之
withInitial()其实是创建一个SuppliedThreadLocal对象,并重写了initialValue()方法
如下:
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
}
withInitial()方法不会给当前线程创建threadlocalMap,只会在用户调用get方法的时候判断thread.map不存在,不存在的话调用SuppliedThreadLocal.initialValue创建threadlocalMap
使用threadlocal 会引起内存泄漏吗
会,但是分场景,手动创建线程,线程用完会销毁的场景不会内存泄漏
线程池的场景中使用threadlocal会引起内存泄漏
但是内存泄漏不是threadlocal的问题,往threadlocalmap里面的entry赋值的时候key被weakrefrence修饰(即weekreference<threadlocal>作为y被赋值给map里的entry)
引用threadlocal的对象被回收了,则threadlocal在下一次GC时也会被回收,但是entry里的value不会被回收,因为该value绑定entry,绑定threadlocalmap,间接的绑定线程,若线程是线程池里面的线程,则线
程不会销毁, 则entry里面的value也不会被销毁,会导致内存泄漏。
而threadlocal早就被gc回收了。
总结:内存泄漏不是threadlocal对象的问题,是因为threadlocalMap.entry 里的value 和线程是强绑定,线程回收,vaule不回收
如何避免上述弱引用引发的内存泄漏?
在使用完ThreadLocal时,及时调用它的的remove方法清除数据。
remove 方法会调用 expungeStaleEntry(int staleSlot) 方法,清除key为空(key就是threadlocal)的entry
总结: 及时清除threadlocal.map里面的vaule 就可以避免内存泄漏,(ThreadLocal的回收不用担心,weekreference修饰,不用的时候会自动GC)
InheritableThreadLocal
解决父线程传递本地变量到子线程的
https://blog.csdn.net/hewenbo111/article/details/80487252
InheritableThreadLocal 是如何在父子线程间传递变量的
因为Thread类有2个变量: ThreadLocal.ThreadLocalMap threadLocals = null; // 线程本地变量
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; //继承线程本地变量
再然后就是线程的构造方法了
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
构造方法里面的init的重载方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {}
最后一个参数: boolean inheritThreadLocals 表示是否需要从父线程继承 inheritableThreadLocals 属性
如下判断:
if (inheritThreadLocals && parent.inheritableThreadLocals != null){ // 如果重载的init方法里的布尔值inheritThreadLocals为true
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);//则将当前子线程的inheritableThreadLocals属性设置
//为父线程的inheritableThreadLocals属性,即把父线程额threadlocalMap 赋值给子线程的 inheritableThreadLocals字段
}
我们知道线程从threadlocal里调用get()方法获取绑定的参数的时候主要是先调用getMap()方法取出对于的threadlocalMap对象
恰好 InheritableThreadLocal 重写了 getMap() 方法
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
若实现类是InheritableThreadLocal对象时getMap方法取的是当前线程的ThreadLocal.ThreadLocalMap inheritableThreadLocals 属性,
而当前线程的该属性已经在构造当前线程对象的时候从父线程里面copy出来了,并且赋值了(详见上面Thread类的重载的init方法)
总结下来就是:
当前线程里面调用threadlocal对象的get()方法取值的时候,如果threadlocal对象是ThreadLocal时,则从threadLocals属性取ThreadLocalMap,
如果threadlocal对象是InheritableThreadLocal时,则从inheritableThreadLocals属性取ThreadLocalMap,
再从取出的Map里面取值。
所以能做到父子线程传递变量
TransmittableThreadLocal
解决线程池里面变量传递的问题
https://blog.csdn.net/hewenbo111/article/details/90053105
基础决定深度啊!