在开发中,我们可能会需要在一个线程中做一个方法栈很深的调用,同时需要在最后的处理结果中给出对应的监听回调。类似的需求比如文件的下载与缓存,涉及到从Activity到服务,到子线程,最后再回到主线程给出回调的操作。对于这种情况有两种处理方式,一种是在方法调用中一层层的传递监听器,但是这样的缺点是会让代码的维护变得复杂。另外一种是设置监听器为static的,这样虽然可以保证在对应的线程中随时改变监听器的状态,但是如过线程太多,静态变量太多,这样看起来会有些不可思议。Android中提供了一种线程内的私有变量的实现---------threadlocal。通过它我们可以在每个线程中,以相同的threadlocal作为key,可以在不同的线程中拿到不同的value。
threadlocal的的分析涉及到三个类。Thread,ThreadLocalMap,ThradLocal。Thread就是线程类,实现了Runnable接口,start之后就会调用run()。ThreadLocalMap是ThradLocal的静态内部类,既然是静态的内部类,其实从逻辑上来说就是和ThradLocal没有什么关系了。但是因为它是Thread类与ThradLocal类的桥梁,所以就放在ThradLocal中的。下面说一下三者的关系:
当我们在一个线程中使用threadlocal进行get或者set操作时,该方法内部会先拿到threadlocal所在的Thread对象。之所以拿这个Thread对象,是为了使用Thread对象中的成员变量 --------> ThreadLocal.ThreadLocalMap threadLocals = null;因为是成员变量,所以每个线程在创建后都有该变量,并且默认值为空。这样,三者之间的联系就建立了。
建立联系后就是具体的使用了,这里先从ThradLocal的set和get方法进行分析:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
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()会先拿到线程对象,从线程对象中拿到ThreadLocalMap对象。然后把当前的ThreadLocal作为key,set方法的参数作为value给设置给ThreadLocalMap中。
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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(),会先拿到线程对象,从线程对象中拿到ThreadLocalMap对象。然后把当前的ThreadLocal作为key,拿到Entry对象,Entry下面我们将会分析,其实和hashmap里面的entry是相同的使用方法。最后从entry从,根据ThreadLocal的值,拿到对应的value,返回给调用者。这样就实现了一个线程对应于多个ThreadLocal,不同的ThreadLocal可以取到各自对应的值。既然一个thread的ThreadLocalMap对象可以对应多个threadlocal,那么不同的线程有不同的ThreadLocalMap对象,所以在不同线程中也就是操作自己的ThreadLocalMap对象,所以就可以实现线程内的私有变量了。说起来,ThreadLocalMap对象的确是线程类的一个成员变量。
下面来聊聊ThreadLocalMap,ThreadLocalMap是用来存储键值对的类。是ThreadLocal的静态内部类,大概代码如下:
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
/**
* The number of entries in the table.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
}
这个类里面有一个Entry数组,key是虚引用的ThreadLocal对象,value是ThreadLocal使用时put的值。从而实现一个线程对应于多个ThreadLocal对象的关系对应。这儿有趣的是,也有一些hash操作,获得索引,再hash,装载因子之类的设置。
关于threadlocal,大概的东西都在这儿了。里面也有很多算法细节挺复杂的,值得研究。