ThreadLocal在Android中的使用场景
当在同一个变量上不同线程保存各自不同的值时,可以使用ThreadLocal,在Android中,与线程间消息传递有关的Looper则使用到ThreadLocal,代码如下:
.......
static final ThreadLocal sThreadLocal=new ThreadLocal();
private static void prepare(boolean quitAllowed) {
if(sThreadLocal.get() !=null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
.........
可见,每一个调用Looper.prepare()的线程,都在ThreadLocal里保存了一个Looper对象。
从ThreadLocal.set()方法开始探究,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);}
首先拿当前线程的对象,然后从当前线程对象拿一个ThreadLocalMap对象,假设第一次使用ThreadLocal,拿不到ThreadLocalMap对象,从而进入createMap(t,value)方法,该方法如下:
void createMap(Thread t,T firstValue) {
t.threadLocals =new ThreadLocalMap(this,firstValue);
}
可见该方法的作用是设置线程的Thread.threadLocals变量为ThreadLocalMap对象,继续进入ThreadLocalMap的源代码,下面只列出关键代码:
static class Entry extendsWeakReference {
Object value;
Entry(ThreadLocal k,Object v) {
super(k);
value= v;
}
}
这是ThreadLocalMap内部类,用于保存值,
private Entry[ ] table;
这是ThreadLocalMap的变量,保存值的队列,
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);
}
这是构造方法,利用ThreadLocal产生数组的下标值,并在该数组位置保存对应值,
private Entry getEntry(ThreadLocal key) {
inti = key.threadLocalHashCode & (table.length-1);
Entry e =table[i];
if(e !=null&& e.get() == key)
return e;
else
return getEntryAfterMiss(key,i,e);
}
这个方法是重点方法,解释了如何获取值,首先利用ThreadLocal产生数组下标,找到该位置的值,并进行比较,如果key一致则直接返回,也有可能不一致,因为利用数组实现Map会有哈希表的冲突出现,通常有两种方法解决,一是链地址法,另一个是开放地址法,进入getEntryAfterMiss(key,i,e)方法查看:
private Entry getEntryAfterMiss(ThreadLocal key, int i,Entry e) {
Entry[] tab =table;
int len = tab.length;
while(e !=null) {
ThreadLocal k = e.get();
if(k == key)
return e;
if(k ==null)
expungeStaleEntry(i);
else
i =nextIndex(i,len);
e = tab[i];
}
return null;
}
该方法有一个循环,循环里面重点是nextIndex(i,len),继续进入
private static int nextIndex(int i, int len) {
return ((i +1< len) ? i +1:0);
}
可见,ThreadLocalMap使用开放地址法查找元素,整个ThreadLocalMap的作用就是根据ThreadLocal作为键去保存及查找对应值。
思路回到Thread上,
ThreadLocal.ThreadLocalMap threadLocals=null;
这是Thread类的代码,可知ThreadLocalMap是保存在Thread类里的变量,其实每个Thread在ThreadLocal上保存的变量就在它自己的成员变量里,再看ThreadLocal的get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if(map !=null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if(e !=null)
return (T)e.value;
}
return setInitialValue();
}
这就是先拿到保存在Thread类自己的成员变量ThreadLocalMap,看getMap(t)方法:
ThreadLocalMap getMap(Thread t) {
returnt.threadLocals;
}
是获取当前线程的变量ThreadLocalMap变量,然后根据ThreadLocalMap以ThreadLocal作为key查找值。
因此ThreadLocal的原理就是,我们可以new 很多ThreadLocal变量,每个线程保存一个不同的值,最终这个值以ThreadLocal为key保存在不同的线程的ThreadLocalMap变量上,当要拿出值时,每个线程最终也是从自己的ThreadLocalMap变量上拿值,所以最终效果就是在同一个ThreadLocal变量上,不同线程可以保存获取不同的值
网友评论