<强>前言强>
ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程可以访问自己内部的副本变量,不同线程之间不会互相干扰。本文会基于实际场景介绍ThreadLocal如何使用以及内部实现机制。
<强>应用场景强>
参数对象的数据需要在多个模块中使用,如果采用参数传递的方式,显然会增加模块之间的耦合性。先看看用ThreadLocal是如何实现模块间共享数据的。
类参数{ 私有静态ThreadLocal,_parameter=new ThreadLocal<的在(); 公共静态参数init () { _parameter。(新参数设置()); } 公共静态参数get () { _parameter.get (); } …省略变量声明 }
-
<李>在模块一个中通过Parameter.init初始化。李>
<李>在模块B或模块C中通过Parameter.get方法可以获得同一线程中模块一个已经初始化的参数对象。李>
<>强实现原理强>
从线程线程的角度来看,每个线程内部都会持有一个对ThreadLocalMap实例的引用,ThreadLocalMap实例相当于线程的局部变量空间,存储着线程各自的数据,具体如下:
,
类条目延伸WeakReference <强> ThreadLocalMap 强> 内部使用餐桌数组存储条目,默认大小INITIAL_CAPACITY(16),先介绍几个参数: <强> ThreadLocal.set()实现强> 从上面代码中看出来: 接下去看看条目存表数入组如何实现的: 1。通过ThreadLocal的nextHashCode方法生成的哈希值。 从nextHashCode方法可以看的出,ThreadLocal每实例化一次,其散列值就原子增加HASH_INCREMENT。 2。通过散列,(len 1)定位到表的位置,假设表中我位置的元素为f。 3。如果f !=null,假设f中的引用为凯西: 4。如果f==null,则把条目加入到我表的位置中。 5。通过cleanSomeSlots删除陈旧的元素,如果表中没有元素删除,需判断当前情况下是否要进行扩容。
<李>大小:表中元素的数量。李>
<李>阈值:表大小的2/3,当大小祝辞=阈值时,遍历表并删除键为空的元素,如果删除后大小祝辞=阈值* 3/4时,需要对表进行扩容。李>
公共空集(T值){
线程t=Thread.currentThread ();
ThreadLocalMap地图=getMap (t);
如果(地图!=null)
地图。集(这个值);
其他的
createMap (t值);
}
ThreadLocalMap getMap(线程t) {
返回t.threadLocals;
}
<李>从当前线程线程中获取ThreadLocalMap实例。李>
<李> ThreadLocal实例和价值封装成条目。李>
私人空集(ThreadLocal<& # 63;比;键,对象值){
条目[]选项卡=表;
int len=tab.length;
int i=键。threadLocalHashCode,(len-1);
(条目e=选项卡(我);e !=零;e=选项卡(i=nextIndex(我,len))) {
ThreadLocal<& # 63;比;k=e.get ();
如果(k==键){
e。值=https://www.yisu.com/zixun/value;
返回;
}
如果(k==null) {
replaceStaleEntry(价值,关键我);
返回;
}
}
选项卡[我]=新条目(键,值);
int深圳=+ +大小;
如果(!深圳cleanSomeSlots(我)& &深圳>=阈值)
重复();
}
私有静态AtomicInteger nextHashCode=new AtomicInteger ();
私有静态int nextHashCode () {
返回nextHashCode.getAndAdd (HASH_INCREMENT);
}
<李>如果k和当前ThreadLocal实例一致,则修改值值,返回。李>
<李>如果k为null,说明这个f已经是过期(陈旧的)的元素。调用replaceStaleEntry方法删除表中所有陈旧的元素(即输入的引用为null)并插入新元素,返回。李>
<李>否则通过nextIndex方法找到下一个元素f,继续进行步骤3。李>
Java ThreadLocal深入浅出的学习