ThreadLocal的使用和实现原理 ThreadLocal是什么?
ThreadLocal提供线程本地变量,每个线程拥有本地变量的副本,各个线程之间的变量互不干扰。ThreadLocal实现在多线程环境下去保证变量的安全。以下来源于ThreadLocal类的注释。
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.
ThreadLocal的使用
JDK中的例子,定一个一个私有的,静态的ThreadLocal变量,并且重写了initialValue方法,每个线程都会调用这个方法,获得初始化值。
JDK中的例子 如果需要我们可以重写initialValue方法,实现自己的业务逻辑。
使用其实很简单,保存值就调用set方法,获得值调用get方法,最后调用remove方法删除数据,防止内存泄漏和数据混乱。
ThreadLocal的数据结构
Thread类中有个变量threadLocals,这个类型为ThreadLocal中的一个内部类ThreadLocalMap,这个类没有实现map接口,就是一个普通的Java类,但是实现的类似map的功能。
ThreadLocal数据结构 每个线程都要自己的一个map,map是一个数组的数据结构存储数据,每个元素是一个Entry,entry的key是threadlocal的引用,也就是当前变量的副本,value就是set的值。
ThreadLocal的源码分析
1、Thread类中有个变量threadLocals,类型为ThreadLocal.ThreadLocalMap,这个就是保存每个线程的私有数据。
threadLocals 2、ThreadLocalMap是ThreadLocal的内部类,每个数据用Entry保存,其中的Entry继承与WeakReference,用一个键值对存储,键为ThreadLocal的引用。为什么是WeakReference呢?如果是强引用,即使把ThreadLocal设置为null,GC也不会回收,因为ThreadLocalMap对它有强引用。
Entry的定义 3、ThreadLocal中的set方法的实现逻辑,先获取当前线程,取出当前线程的ThreadLocalMap,如果不存在就会创建一个ThreadLocalMap,如果存在就会把当前的threadlocal的引用作为键,传入的参数作为值存入map中。
set方法 4、ThreadLocal中get方法的实现逻辑,获取当前线程,取出当前线程的ThreadLocalMap,用当前的threadlocak作为key在ThreadLocalMap查找,如果存在不为空的Entry,就返回Entry中的value,否则就会执行初始化并返回默认的值。
get方法 5、ThreadLocal中remove方法的实现逻辑,还是先获取当前线程的ThreadLocalMap变量,如果存在就调用ThreadLocalMap的remove方法。ThreadLocalMap的存储就是数组的实行,因此需要确定元素的位置,找到Entry,把entry的键值对都设为null,最后也Entry也设置为null。其实这其中会有哈希冲突,具体见下文。
remove方法 解决哈希冲突
ThreadLocal中的hash code非常简单,就是调用AtomicInteger的getAndAdd方法,参数是个固定值0x61c88647。
上面说过ThreadLocalMap的结构非常简单只用一个数组存储,并没有链表结构,当出现Hash冲突时采用线性查找的方式,所谓线性查找,就是根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。如果产生多次hash冲突,处理起来就没有HashMap的效率高,为了避免哈希冲突,使用尽量少的threadlocal变量
最后
使用场景:一些ORM框架的Session管理,web系统的会话管理等
简单总结:一个ThreadLocal只能保存一个变量的副本,如果需要多个,就得创建多个变量;我们确定使用完需要执行remove避免内存泄漏。