网站设计的基本知识,seo对网站的作用,班级网站建设的内容,网站建设报价单表格模板问#xff1a;谈一谈ThreadLocal的结构。
ThreadLocal内部维护了一个ThreadLocalMap静态内部类#xff0c;ThreadLocalMap中又维护了一个Entry静态内部类#xff0c;和Entry数组。
Entry类继承弱引用类WeakReference#xff0c;Entry类有一个有参构造函数#xff0c;参数…问谈一谈ThreadLocal的结构。
ThreadLocal内部维护了一个ThreadLocalMap静态内部类ThreadLocalMap中又维护了一个Entry静态内部类和Entry数组。
Entry类继承弱引用类WeakReferenceEntry类有一个有参构造函数参数为ThreadLocal和value值构造方法函数内部会调用父类有参构造函数ThreadLocal作为父类有参构造函数的参数。
其底层数据结构可以看成是一个hash表索引是通过原子类AtomicInteger、HASH_INCREMENT 0x61c88647和Entry[ ]的长度而来。
索引 ThreadLocal#nextHashCode (Entry[]#length - 1)nextHashCode AtomicInteger#getAndAdd(HASH_INCREMENT)问你知道ThreadLocal是如何保证线程隔离的么
在Thread内部维护了ThreadLocal.ThreadLocalMap这个对象threadLocals在Thread.currentThread获取当前线程时会初始化当前线程的threadLocals。
也就是说每个Thread内部都有一个ThreadLocalMap,ThreadLocalMap伴随着Thread的整个生命周期也会随着线程的销毁而终结 问你知道ThreadLocal和Synchronized的区别吗
都能对数据进行线程隔离吧Synchronized是用时间换空间ThreadLocal使用空间换时间。
问为什么ThreadLocal所谓的KeyThreadLocal为弱引用为什么Value不能为弱引用对象呢
对于keyThreadLocal为弱引用问题 如果key为强引用引用的ThreadLocal的对象被回收了但是ThreadLocalMap还持有ThreadLocal的强引用如果没有手动删除ThreadLocal不会被回收导致Entry内存泄漏。
反之如果key为弱引用 引用的ThreadLocal的对象被回收了由于ThreadLocalMap持有ThreadLocal的弱引用即使没有手动删除ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,getremove的时候会被清除。
对于value为什么为强引用不使用弱引用的问题如果value为弱引用当value对象被回收了ThreadLocalMap还持有value的弱引用也会被回收这样就会出现存在key值而value值不存在这样的情况是不允许的。所以使用value使用强引用当key被回收掉在调用set、get、remove方法时会将key失效的value值清除掉。
问ThreadLocal会造成内存泄漏么
使用完ThreadLocal没有正确的调用remove方法去清理就会造成内存泄漏虽然调用set和get方法的时候也会清理失效的key和对应的value但是这并不是及时的比如下面这个案例
每个线程恰好只使用了一次set方法没有及时地调用remove方法这样很容易造成内存泄露知道内存溢出。 import java.util.concurrent.*;
/*** -Xmx5m :设置堆内存为5m*/
public class ThreadLocalTest {static ThreadLocalbyte[] threadLocal new ThreadLocal();static ThreadPoolExecutor executorService new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS,new LinkedBlockingDeque(100),Executors.defaultThreadFactory());public static void main(String[] args) {executorService.execute(() - {threadLocal.set(new byte[1024 * 1024 * 2]);});executorService.execute(() - {threadLocal.set(new byte[1024 * 1024 * 2]);});}
}问那如何才能正确的使用ThreadLocal呢
用 private fianl static 修饰主要使用为threadLocal作为每个线程内部的map的key所以不需要总是创建维持一个就可以再者是因为static修饰其就属于类本身生命周期跟随类一致。因为ThreadLocal作为key并且为弱引用所以用private fianl static修饰会防止threadLocal被gc掉防止内存泄漏。当然在finally块中调用ThreadLocal#remove方法就显得尤为重要。否则不仅可能会造成内存泄漏在使用线程池的情况下还可能会读到脏数据。
问那你谈谈ThreadLocal底层的这个hash表
这个hash表是一个Entry[]默认容量的是16扩容因子是2/3通过开放寻址法解决hash冲突每次的索引值是通过如下得到伪代码
索引 ThreadLocal#nextHashCode (Entry[]#length - 1)// 保证索引的原子性
nextHashCode AtomicInteger#getAndAdd(HASH_INCREMENT)HASH_INCREMENT魔术值为0x61c88647这个数是通过斐波那契散列求出来的
魔数 黄金分割比 (0.618)*2^32每次扩容都会扩大2倍这样的好处是减少Hash碰撞让数据更散列更均匀的分布更充分的利用数组的空间。
原因如下 当数组的长度为2的幂次方时len - 1的二进制为1...1...1做运算时取决于key.threadLocalHashCode也就是说key.threadLocalHashCode本身符合均匀分布Hash算法的结果就是均匀的。
索引 key.threadLocalHashCode (len - 1)在set方法的时候如果发现有失效的key就会去清除失效的key和对应的value。 清理的逻辑是 从数组的当前坐标失效key向前遍历找到最前面的失效的key记录下来假设此位置为a。 再从数组的当前坐标失效key向后遍历此时 如果遇到同样的key就先替换再开始从记录下来的位置a到此位置开始清理清理过程中如果发现此区间内存在有效的key那么将这些key重新hash放到别的位置上因为采用了开放寻址法如果没遇到同样的key找到最后的失效的key在这个区间开始清理。 问你知道父线程和子线程如何共享ThreadLocal吗
可以用InheritableThreadLocal InheritableThreadLocal实现子线程可以访问父线程的线程变量的实现原理如下 InheritableThreadLocal通过重写createMap 和 getMap 方法让本地变量保存到了具体线程的inheritableThreadLocal变量中线程通过调用inheritableThreadLocal实例的set或get方法时就会创建当前线程的inheritableThreadLocal变量当父线程创建子线程时构造函数会把父线程中的inheritableThreadLocal变量里面的本地变量值复制一份保存到子线程的inheritableThreadLocal变量里 案例
public class ThreadLocalTest {static InheritableThreadLocalString threadLocal new InheritableThreadLocal();static ThreadPoolExecutor executorService new ThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,new LinkedBlockingDeque(100),Executors.defaultThreadFactory());public static void main(String[] args) {threadLocal.set(hello);executorService.execute(() - {System.out.println(threadLocal.get() 1);});executorService.execute(() - {System.out.println(threadLocal.get() 2);});}
}参考资料
spring.io 京东云官方blog 货拉拉官方blog ThreadLocal源码解析