这样的话,在get的时候,也会根据ThreadLocal对象的hash值,定位到table中的位置,然后判断该位置Entry对象中的key是否和get的key一致,如果不一致,就判断下一个位置,set和get如果冲突严重的话,效率还是很低的。
以下是get的源码,是不是就感觉很好懂了:
privateEntrygetEntry(ThreadLocal<?>key){
inti=key.threadLocalHashCode&(table.length-1);
Entrye=table[i];
if(e!=null&&e.get()==key)
returne;
else
returngetEntryAfterMiss(key,i,e);
}
privateEntrygetEntryAfterMiss(ThreadLocal<?>key,inti,Entrye){
Entry[]tab=table;
intlen=tab.length;
//get的时候一样是根据ThreadLocal获取到table的i值,然后查找数据拿到后会对比key是否相等if(e!=null&&e.get()==key)。
while(e!=null){
ThreadLocal<?>k=e.get();
//相等就直接返回,不相等就继续查找,找到相等位置。
if(k==key)
returne;
if(k==null)
expungeStaleEntry(i);
else
i=nextIndex(i,len);
e=tab[i];
}
returnnull;
}
能跟我说一下对象存放在哪里么?
在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存,而堆内存中的对象对所有线程可见,堆内存中的对象可以被所有线程访问。
那么是不是说ThreadLocal的实例以及其值存放在栈上呢?其实不是的,因为ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有),而ThreadLocal的值其实也是被线程实例持有,它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。
如果我想共享线程的ThreadLocal数据怎么办?使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
privatevoidtest(){
finalThreadLocalthreadLocal=newInheritableThreadLocal();
threadLocal.set("帅得一匹");
Threadt=newThread(){
@Override
publicvoidrun(){
super.run();
Log.i("张三帅么=" threadLocal.get());
}
};
t.start();
}
在子线程中我是能够正常输出那一行日志的,这也是我之前面试视频提到过的父子线程数据传递的问题。
怎么传递的呀?传递的逻辑很简单,我在开头Thread代码提到threadLocals的时候,你们再往下看看我刻意放了另外一个变量:
Thread源码中,我们看看Thread.init初始化创建的时候做了什么:
publicclassThreadimplementsRunnable{
……
if(inheritThreadLocals&&parent.inheritableThreadLocals!=null)
this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
……
}
我就截取了部分代码,如果线程的inheritThreadLocals变量不为空,比如我们上面的例子,而且父线程的inheritThreadLocals也存在,那么我就把父线程的inheritThreadLocals给当前线程的inheritThreadLocals。
是不是很有意思?
你是说内存泄露么?
我丢,这小子为啥知道我要问什么?嗯嗯对的,你说一下。这个问题确实会存在的,我跟大家说一下为什么,还记得我上面的代码么?
ThreadLocal在保存的时候会把自己当做Key存在ThreadLocalMap中,正常情况应该是key和value都应该被外界强引用才对,但是现在key被设计成WeakReference弱引用了。