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

來源:互聯網
上載者:User

標籤:class   stat   靜態   初始化   mic   subclass   main   public   strong   

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

 

先看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的工作原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.