部分内容来自黑马和javaguide,有版权问题请联系我
ThreadLocal对象可以提供线程局部变量,每个线程Thread拥有一份自己的副本变量,多个线程互不干扰。
Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap。
ThreadLocalMap有自己的独立实现,可以简单地将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本身,而是它的一个弱引用)。
每个线程在往ThreadLocal里放值的时候,都会往自己的ThreadLocalMap里存,读也是以ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
ThreadLocalMap有点类似HashMap的结构,只是HashMap是由数组+链表实现的,而ThreadLocalMap中并没有链表结构。
我们还要注意Entry, 它的key是ThreadLocal<?> k ,继承自WeakReference, 也就是我们常说的弱引用类型。
注意:当Thread销毁时,ThreadLocalMap也会销毁,减少内存的使用。
Java的四种引用类型:
如果ThreadLocal使用强引用的话:
可以看到,ThreadLocal类无法被回收,造成内存泄漏。
为什么使用弱引用?
ThreadLocal可以被回收,但是Entry中的value仍然无法被回收,仍旧会导致内存泄漏。
原因与解决:
事实上,在ThreadLocalMap中的set/getEntry方法中,会对Key值为null进行判断,如果Key为null,会对value置null,相当于释放value空间。所以实际上弱引用比强引用多一层保障,即使不不手动调用remove方法回收Entry空间,下一次调用set,get中的任一方法时都会回收value 的空间,从而避免内存泄漏。
int i = key.threadLocalHashCode & (len-1);
ThreadLocalMap中hash算法很简单,这里i就是当前key在散列表中对应的数组下标位置。
这里最关键的就是threadLocalHashCode值的计算,ThreadLocal中有一个属性为HASH_INCREMENT = 0x61c88647,每当创建一个ThreadLocal对象,这个ThreadLocal.nextHashCode 这个值就会增长 0x61c88647 。这个值很特殊,它是斐波那契数 也叫 黄金分割数。hash增量为 这个数字,带来的好处就是 hash 分布非常均匀。