看了下ThreadLocal的源码,对这个有了一个基本的了解。
只简单的追了一下源码,解析得不到位,各位要深入了解的可以看看源码和看看大佬的解析。
ThreadLocal提供了线程的局部变量,让每个线程通过get/set来操作这个局部变量;不会和其他线程的局部变量冲突,实现线程的数据隔离。
主要涉及到4个类,分别是:Thread、ThreadLocal、ThreadLocalMap、Entry。
其中ThreadLocalMap是ThreadLocal的静态内部类,Entry是ThreadLocalMap的静态内部类。
ThreadLocal类中,主要的3个方法:T get()、void set(T value)、void remove()。
分别代表 取数据、存数据、删除数据。
先把Thread类的源码放这里,里面有这样2个属性,后面会用到。
ThreadLocal可以让每个线程又自己的私有数据 =》 threadLocals
InheritableThreadLocal可以让子线程获取到父线程的私有数据 =》 inheritableThreadLocals
set方法
这个流程很容易理解,首先获取当前线程,然后获取线程中的threadLocals属性,返回的就是一个ThreadLocalMap,然后判断是否为null,创建或者调用ThreadLocalMap的set方法设置值,初始的时候都是null的,初次使用或者在构建时进行初始化。
可以看到,ThreadLocalMap的set方法,将ThreadLocalMap作为key,值作为value封装成一个Entry对象进行存储,存的地方就是上面的 private Entry[] table 里面,其中key是弱引用。
get
也是先获取当前线程,从当前线程中获取到ThreadLocalMap属性;如果map是null的,就设置个初始化的值(也就是null);
如果不是null的,计算下标,从table中获取对应的Entry对象,从中取值;
如果Entry存在,并且引用还存在(key是弱引用,容易被回收)且是传入的ThreadLocalMap引用,直接返回该Entry对象;
会清除不用的数据。
remove:
还是根据当前线程获取对应的ThreadLocalMap,调用ThreadLocalMap的remove方法;
清除数据
InheritableThreadLocal
InheritableThreadLocal继承自ThreadLocal,仅重写了一下3个方法
创建线程时,如果父线程的inheritableThreadLocals不是null,会将父线程的inheritableThreadLocals传递给子线程
子线程将父线程的所有变量复制一份
InheritableThreadLocal在线程池中使用要特别注意。它是在初始化的时候复制的父线程的数据,后期就不会主动变化了,所以在第一次调用线程池的线程执行任务时初始化,后面即使再调用,也只是第一次调用时的父线程的数据。
通过这个来获取比如当前用户信息等就可能出现问题。
后续补充:
今天(2021-12-28)有看到一篇文章写的是netty的FastThreadLocal,这玩意儿用法几乎跟JDK的ThreadLocal没啥区别,只是不能使用JDK的Thread,而是要使用netty的FastThreadLocalThread,这个类继承自Thread;FastThreadLocal用好了据说要比JDK的ThreadLocal快很多。
参考原文:https://blog.youkuaiyun.com/lirenzuo/article/details/94495469;
https://juejin.cn/post/7023921712467017741