ThreadLocal的原理核心就是理顺Thread,ThreadLocal以及ThreadLocalMap三者之间的关系。即每个Thread对象中都持有一个ThreadLocalMap成员变量,ThreadLocalMap可以持有多个ThreadLocal和对应的value。当往ThreadLocalMap 设置值时候,key是threadLocal本身。
下面,通过对其核心的三个方法的流程来理解其实现过程。
initialValue()方法处理流程分析
initialValue()方法会返回当前线程对应的“初始值”,这是一个延迟加载的方法,只有在调用get的时候,才会触发当线程第一次使用get方法访问变量时,将调用此方法。但是如果线程调用了set方法,在这种情况下,不会调用initialValue方法了。
<code>protected T initialValue() {
return null;
}/<code>
默认的initialValue方法是没做任何事情的,所以我们要重写initialValue方法,就像前面的例子。
Set流程分析
Set方法是设置值。
Threadlocal.set()
<code>public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//通过当前线程,获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//如果ThreadLocalMap已经存在,则将值传进去
map.set(this, value);
else
//如果ThreadLocalMap不存在,则创建ThreadLocalMap
createMap(t, value);
}/<code>
这里设置值是设置到Thread的ThreadLocalMap中,所以需要获取当前的Thread。
Threadlocal.getMap方法
<code>ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}/<code>
这个方法很简单,就是获取线程中的成员变量ThreadLocalMap
在Thread中成员变量threadLocals就是ThreadLocal.ThreadLocalMap 类型,初始值为null。
Threadlocal.createMap方法
<code>void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}/<code>
可以看到,createMap方法就是为Thread的成员变量threadLocals 创建实例。并将需要设置的值放进map中。key就是threadlocal本身,value是需要保存的值。
Get流程分析
Get获取设置的值
ThreadLocal.get方法
<code>public T get() {
//获取当前的线程
Thread t = Thread.currentThread();
//通过当前线程,获取当前线程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//如果ThreadLocalMap不为空,获取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果ThreadLocalMap为空,返回初始值
return setInitialValue();
}/<code>
这里要注意的是Entry 继承了弱引用类型,使用弱引用类型的目的是为了避免已经无用的数据占用内存空间。当一个对象仅仅被weak reference指向, 而没有任何其他强引用指向, GC运行时候这个对象就会被回收。
Threadlocal.Entry 类型
<code>static class Entry extends WeakReference<threadlocal>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}/<threadlocal>/<code>
<code>弱引用也是关键的知识。下面的代码可以测试弱引用是否有强引用时候的区别
public class TestWeakReference {
public static void main(String[] args) {
Car car = new Car(22000,"silver");
WeakReferenceweakCar = new WeakReference /<code>(car);
int i=0;
while(true){
if(weakCar.get()!=null){
i++;
System.out.println("Object is alive for "+i+" loops - "+weakCar);
}else{
System.out.println("Object has been collected.");
break;
}
}
//如果有下面这一句,car就不会被回收。因为被强应用指向了
//System.out.println(car);
}
}
如果注释掉打印“System.out.println(car)”这一行,过了一段时间GC后,会获取到null的car值,然后会打印“Object has been collected.”
閱讀更多 IT技術研習社 的文章
關鍵字: 源码 ThreadLocal 打印