1. 程式人生 > >【原創】源碼角度分析Android的消息機制系列(三)——ThreadLocal的工作原理

【原創】源碼角度分析Android的消息機制系列(三)——ThreadLocal的工作原理

沒有 cit gen 管理 pre 靜態 bsp 允許 clas

ι 版權聲明:本文為博主原創文章,未經博主允許不得轉載。

先看Android源碼(API24)中對ThreadLocal的定義:

public class ThreadLocal<T> 

即ThreadLoca是一個泛型類,再看對該類的註釋:

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
 * copy of the variable.  <tt>ThreadLocal</tt> instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread‘s id is assigned the first time it invokes <tt>ThreadId.get()</tt>
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread‘s ID
 *     private static final ThreadLocal&lt;Integer> threadId =
 *         new ThreadLocal&lt;Integer>() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread‘s unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the <tt>ThreadLocal</tt>
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 *
 * 
@author Josh Bloch and Doug Lea * @since 1.2 */

也就是說,ThreadLocal類提供一個thread-local的變量,但是這個變量在每個線程中的副本是不同的,每個線程獨立地使用thread-local變量在自己線程中的副本。ThreadLocal的實例是private static的,並且該實例是和一個線程的狀態相關的。每個線程持有thread-local變量的弱引用。線程死亡,線程中所有thread-local實例的副本會被GC回收(除非該副本存在一些其他引用。因為GC回收一個對象的判定標準是,該對象不存在任何引用或被引用的關系)。

只需要弄清楚ThreadLocal的get和set方法,就可以明白其工作原理了。

先看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); }

value即要存儲的數據。ThreadLocalMap 是ThreadLocal中的一個內部類,主要用來存儲threadLocal中的數據,下面會詳細說明。通過上面這段代碼,我們可以知道,set方法首先會獲取當前線程的ThreadLocalMap。如果map不為空,則直接更新數據;否則,創建ThreadLocalMap,同時將value值放入該map中。

若想要給thread-local變量一個初始值的話,不需要重寫set方法,直接重寫initialValue方法即可。

protected T initialValue() {
    return null;
}

一般情況下,當調用get方法時,該方法才會被第一次調用,除非在調用get方法之前,先調用了set方法。

下面我們來看下ThreadLocalMap:

    /**
     * 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

ThreadLocalMap是ThreadLocal中的一個靜態內部類,為了維護threadLocal中的數據而特意定制的一個hash map。Hash table中的entry使用了弱引用。因為這裏沒有用引用隊列,所以只有當hash table內沒有空間了,才會將entry remove出去。

ThreadLocalMap也有一個靜態內部類:

static class Entry extends WeakReference<ThreadLocal> {
     /** The value associated with this ThreadLocal. */
     Object value;
 
     Entry(ThreadLocal k, Object v) {
         super(k);
         value = v;
     }
 }

Entry.value即我們存儲的數據。

private Entry[] table;

我們將存儲數據的Entry都存放到該table中了。進而通過對table的管理去管理存儲的數據。

再來看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();
 }

通過源碼我們可以知道,get方法也是先要獲取ThreadLocalMap ,若ThreadLocalMap 不為空,則獲取其內部的Entry,由上面我們對set方法的分析可以知道,Entry以弱引用的方式存儲了value。若Entry不為空,我們將Entry中的value直接返回,即可獲得ThreadLocal中存儲的數據;否則,就返回ThreadLocal中的初始化數據。

由上面對ThreadLocal的set和get方法的分析,我們可以看出,我們操作的始終是當前線程的ThreadLocalMap,存放的數據在Entry中,table中又存放了大量的Entry,對Entry進行管理,而table數組又在當前線程的ThreadLocalMap,所以我們在不同線程中訪問同一個ThreadLocal的set和get方法時,它們對ThreadLocal的讀/寫操作都僅僅是在各自線程的內部而已。這就解釋了為什麽ThreadLocal可以在多個線程中互不幹擾地存儲和修改數據了。

【原創】源碼角度分析Android的消息機制系列(三)——ThreadLocal的工作原理