我们在研究Handler曾经说过,线程与Looper的对应关系是通过ThreadLocal来实现的,那么ThreadLocal内部是怎么做的呢?其实说到一一对应的关系,我们普遍都会想到Map键值对的存储,那么就让我们再次来的源码的世界,看看ThreadLocal的实现究竟与Map有没有关系:
public class ThreadLocal {
.....
/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}
/**
* 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.
*返回当前线程副本中该线程局部变量的值。
*如果变量没有当前线程的值,则首先将其初始化为调用{@link #initialValue}方法返回的值。
*
* @return the current thread's value of this thread-local
*/
public T get() {
//获取当前线程以及线程内部的ThreadLocalMap 对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//以当前ThreadLocal为key查找并返回数据
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//获取线程内部的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//建立键值对关系
map.set(this, value);
else
//创建ThreadLocalMap 对象并建立键值对关系
createMap(t, value);
return value;
}
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the {@code initialValue} method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* This implementation simply returns {@code null}; if the
* programmer desires thread-local variables to have an initial
* value other than {@code null}, {@code ThreadLocal} must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
/**
* 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.
*将当前线程的这个线程局部变量的副本设置为指定的值。
*大多数子类将不需要重写这个方法,仅仅依靠{@link #initialValue}方法来设置线程局部变量的值。
* @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
ThreadLocalMap map = getMap(t);
if (map != null)
//建立键值对关系
map.set(this, value);
else
//创建ThreadLocalMap 对象并建立键值对关系
createMap(t, value);
}
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
//获取线程内部的ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//移除当前线程对应的数据
m.remove(this);
}
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*创建与ThreadLocal关联的映射。
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
.....
}
通过对get()、set()以及对remove()方法的研究,我们可以得出以下结论:
1、线程Thread类内部持有一个ThreadLocalMap成员变量,此集合就是保存线程数据的地方,ThreadLocalMap的键是ThreadLocal对象,值是传入的T泛型数据。
2、ThreadLocal其实更像一个操作线程Thread成员变量ThreadLocalMap的工具类,ThreadLocal对外的接口内部逻辑基本上都是对ThreadLocalMap数据的增删改查。
ThreadLocal线程数据的独立就是由上述逻辑所实现。看完ThreadLocal的核心代码,我们再来说说ThreadLocalMap这个东西,ThreadLocalMap跟接口Map没有任何关系, ThreadLocalMap是一个定制的散列映射,只适用于维护线程本地值。以下是ThreadLocalMap的源码,我们主要看getEntry()、set()、remove()这三个方法,大体了解一下内部逻辑就行:
public class ThreadLocal {
......
/**
* ThreadLocalMap是一个定制的散列映射,只适用于维护线程本地值。
* 没有任何操作被导出到ThreadLocal类之外。类是包私有的,允许在类线程中声明字段。
* 为了帮助处理非常大且长期存在的用法,哈希表条目对键使用weakreference。
* 但是,由于没有使用引用队列,所以只有在表空间不足时才会删除陈旧的条目。
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference> {
/** 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;
/**
* The number of entries in the table.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal