ThreadLocal
解决的问题:
主要解决的是数据隔离,在多线程的情况下防止自己的变量被修改 。见名知意本地线程,也就是说每个线程都会在ThreadLocal中存放一份属于自己独有的数据,所以它是线程安全的。
相当于Web的域对象,session的作用域是一个会话,而ThreadLocal的作用域就是基本线程。
基本数据结构:
ThreadLocal底层是通过ThreadLocalMap静态内部类实现的,这个ThreadLocalMap就是一个KV的Map集合,底层是一个Entry对象数组,Entry对象存放的K-V分别是ThreadLocal对象和一个Object的值。
数据进行使用的主要方法:
public void set(T var1) {
Thread var2 = Thread.currentThread();//首先获取当前线程
//因为Entry对象存的是ThreadLocal和vlaue
ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
if (var3 != null) {
//不为空的话进行数据存储
var3.set(this, var1);
} else {
//否则进行创建这个ThreadLocalMap集合
this.createMap(var2, var1);
}
}
public T get() {
Thread var1 = Thread.currentThread();
//根据当前线程去获取ThreadLocal.ThreadLocalMap 类型的 threadLocals属性
ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
if (var2 != null) {
//不为空进行获取对象
ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);
if (var3 != null) {
Object var4 = var3.value;
return var4;
}
}
//否则进行初始化当前线程
return this.setInitialValue();
}
散列方式:
斐波那契散列
处理Hash冲突的方式:
在set时候如果遇到Hash冲突,使用线性探测法尝试在数组的索引位置进行存储,同时会释放K为null和Value不为null的Entry数组,防止内存泄漏。
如何处理扩容问题:
默认的ThreadLocalMap的大小为16
private static final int INITIAL_CAPACITY = 16;
判断扩容首先会看Entry数组的长度是否大于原始的三分之二,如果大于进行rehash
private void rehash() {
this.expungeStaleEntries();
//最后判断这个条件,满足了 就进行扩容
if (this.size >= this.threshold - this.threshold / 4) {
this.resize();
}
}
扩容为原来的二倍大小。
如何实现父子线程间局部变量共享问题:
就是实现局部变量直接变量传递:提供了一个InheritableThreadLocal类:可继承线程本地,此类继承的是ThreadLocal类
此时就要翻到Thread类的init方法有这么一段:
如果InheritableThreadLocal不为null,
跟踪代码一直跳到这段方法里面:
private ThreadLocalMap(ThreadLocal.ThreadLocalMap var1) {
this.size = 0;
ThreadLocal.ThreadLocalMap.Entry[] var2 = var1.table;
int var3 = var2.length;
this.setThreshold(var3);
this.table = new ThreadLocal.ThreadLocalMap.Entry[var3];
//主要在这个循环体里面处理
for(int var4 = 0; var4 < var3; ++var4) {
ThreadLocal.ThreadLocalMap.Entry var5 = var2[var4];
if (var5 != null) {
ThreadLocal var6 = (ThreadLocal)var5.get();
if (var6 != null) {
Object var7 = var6.childValue(var5.value);
ThreadLocal.ThreadLocalMap.Entry var8 = new ThreadLocal.ThreadLocalMap.Entry(var6, var7);
int var9;
for(var9 = var6.threadLocalHashCode & var3 - 1; this.table[var9] != null; var9 = nextIndex(var9, var3)) {
}
this.table[var9] = var8;
++this.size;
}
}
}
}
为什么会发生内存泄漏:
什么是内存泄漏:是内存中的数据没有被释放或者没法释放,导致系统资源的浪费,造成不可收拾的后果。
发送原因:
因为Entry继承的是WeakReference若引用,而弱引用在系统发生垃圾回收时候会被回收掉,一般会回收那如何会产生内幕才能泄漏;如果是创建ThreadLocal类的线程一直持续运行,那么Entry对象的key就依旧具有强引用而不会被回收而发生内存泄漏。比如线程池里面的线程都是复用的,之前的处理结束,线程而非死亡,处于复用的线程依然存活,
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> var1, Object var2) {
super(var1);
this.value = var2;
}
}
如何解决内存泄漏:
在代码的最后使用remove就好了,我们只要记得在使用的最后用remove把值清空就好了。
ThreadLocal threadLocal = new ThreadLocal();
try{
threadLocal.set("小明");
}catch (Exception e){
e.printStackTrace();
}finally {
threadLocal.remove();
}