原创源码角度分析Android的消息机制系列——ThreadLocal的工作原理

将相本无种,男儿当自强。这篇文章主要讲述原创源码角度分析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& 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). * * @authorJosh Bloch and Doug Lea * @since1.2 */

也就是说,ThreadLocal类提供一个thread-local的变量,但是这个变量在每个线程中的副本是不同的,每个线程独立地使用thread-local变量在自己线程中的副本。ThreadLocal的实例是private static的,并且该实例是和一个线程的状态相关的。每个线程持有thread-local变量的弱引用。线程死亡,线程中所有thread-local实例的副本会被GC回收(除非该副本存在一些其他引用。因为GC回收一个对象的判定标准是,该对象不存在任何引用或被引用的关系)。
 
【原创源码角度分析Android的消息机制系列——ThreadLocal的工作原理】只需要弄清楚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 = https://www.songbingjia.com/android/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可以在多个线程中互不干扰地存储和修改数据了。
 

    推荐阅读