【原創】源碼角度分析Android的消息機制系列(三)——ThreadLocal的工作原理
ι 版權聲明:本文為博主原創文章,未經博主允許不得轉載。
先看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<Integer> threadId = * new ThreadLocal<Integer>() { * @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的工作原理