您的位置:首页 > 其它

ThreadLocal的源码分析

2016-06-14 13:41 302 查看

概序

如果你还不知道ThreadLocal是什么东西,请看上一篇 ThreadLocal的快速入门

这篇将会讲到ThreadLocal的一些api,然后从源码分析ThreadLocal的工作原理,关于ThreadLocal的使用场景比较少,比如Loop、ActivityThread、ActivityManagerService

ThreadLocal的4个常用api

initialValue() :返回此线程局部变量的当前线程的“初始值”。

get():返回此线程局部变量的当前线程副本中的值。

set():返回此线程局部变量的当前线程副本中的值。

remove():移除此线程局部变量当前线程的值。

使用ThreadLocal最重要是了解set()方法和get()方法

set():

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}


从源码中可以看出,set方法首先是获取当前线程,然后将当前线程传给getMap()方法,返回一个ThreadLocalMap对象

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}


getMap()方法调用线程的threadLocals,通过查看Thread类发现默认为null,没有做实现,所以会调用createMap()方法,

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}


createMap方法中通过当前线程和要设置的值value new了一个ThreadLocalMap,并且将new的实例赋值给当前线程的threadLocals

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}


从源码可以看出通过key和value构造一个Entry对象,并且放到table数组里面。这里的table比较重要,后面的get方法也会用到。这里我们已经了解了set方法的工作原理

get():

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}


get()方法首先是获取当前的线程,然后调用getMap(t);通过当前线程获取值

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}


getMap()方法通过当前线程获取threadLocals,由于之前调用了set方法,所以此时的threadLocals不会为null。在threadLocals不为null的情况下,调用getEntry()方法获取值,如果值不为null的情况下,返回;如果上面条件不满足的话,调用setInitialValue()方法,

private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}


他会调用initialValue()进行初始化一个value,然后和set方法类似的了。到这里get方法的实现原理我们也了解了。

最后再看看另外两个api

initialValue():线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。 这点可以从get方法的源码中可以看出

remove(): 如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用其 initialValue() 方法重新初始化其值。这将导致在当前线程多次调用 initialValue 方法,看如下代码。

public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}


ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}


/**
* Remove the entry for key.
*/
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}


OK,到这里,ThreadLocal的分析将全部完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: