Android_ThreadLoacl原理

先看看google的定义对ThreadLocal 有个大概了解,再进行源码的梳理:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

这里就简单解释下:ThreadLoacl 类为不同线程提供了不同的线程本地变量,他通过get()和set()方法来操作,ThreadLocal的实例在一个类中的典型访问类型是 private static。

在之前的主线程创建Looper代码梳理的过程中,我们就看到有ThreadLocal的身影。看看:


public final class Looper {
    ...
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    ....
    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));
    }
    ...
}

我们知道threadLocal 的主要是通过set()和get()方法来操作线程本地变量的:那就先看看set()和get()方法是入手了解:

ThreadLocal.set()

    /**
     * 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);
    }

我们先看看ThreadLocal的set()方法做了什么?set()中一开始先获取当前thread对象,然后通过getMap()方法拿到一个Map,最后将当前的额thread为key,和传入的Value一起set到map中。接下来看看这个getMap()方法里面是怎么处理的:

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

在getMap()方法中返回来当前线程对象的一个threadLocals,继续看看这个threadLocals是什么

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

通过注解翻译一下;保存与此线程相关的ThreadLocal值,这个Map是由ThreadLocal类维护.那我们就需要到ThreadLocal中看看这个Map.

/**
* 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 {
   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;
    ...
        /**
        * Get the entry associated with key.  This method
        * itself handles only the fast path: a direct hit of existing
        * key. It otherwise relays to getEntryAfterMiss.  This is
        * designed to maximize performance for direct hits, in part
        * by making this method readily inlinable.
        *
        * @param  key the thread local object
        * @return the entry associated with key, or null if no such
        */
    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }
    ...
    private void set(ThreadLocal<?> key, Object value) {
        // We don't use a fast path as with get() because it is at
        // least as common to use set() to create new entries as
        // it is to replace existing ones, in which case, a fast
        // path would fail more often than not.
        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)]) {
             ThreadLocal<?> k = e.get();
             if (k == key) {
                e.value = value;
                return;
            }

            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }

        tab[i] = new Entry(key, value);
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }
    ...
}

ThreadLoaclMap是ThreadLocal的一个静态内部类,根据注解,我们了解到这是一个定制的Map,是为了帮助处理非常大且使用寿命长的用途,hash table 使用WeakReferences作为key,来存储Value的。该Map的大小一定是2的幂,然后我们主要关注set()和getEntery()方法;set()是存,getEntry()就是取;在set()方法中;首先拿到一个表,表的大小是2的幂,然后拿到当前key的threadLocalHashCode与上表的长度减一来拿到一个值,通过这值来拿到一个Entry对象,将Value保存在Entry中key对应的value中。

 

ThreadLocal.get()

我们已经把Value保存起来,现在看看是怎么取出来的。那就看看ThreadLocald的get()方法。

    /**
     * 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();
    }
    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }

在get()方法中,首先也是获取当前线程对象,然后通过getMap()方法拿到ThreadLocalMap,接着通过getEntry()方法从Map中取出Entry,在从Entry中取出保存在里面的Value。最后就是返回取出的Value。在这里如果Entry还不存在,说明之前还没有保存value进去,那么此时就要调用setInitialValue()【setInitialValue()主要工作就是构建Map,然后返回NULL】

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值