ThreadLocal分析

在开发中,我们可能会需要在一个线程中做一个方法栈很深的调用,同时需要在最后的处理结果中给出对应的监听回调。类似的需求比如文件的下载与缓存,涉及到从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,大概的东西都在这儿了。里面也有很多算法细节挺复杂的,值得研究。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值