前言
此源码系是针对Java源码进行的分析,多是个人学习过后的理解,会存在错误或误差,还请指出更正友好交流。
一、ThreadLocal是什么?
ThreadLocal最开始看到便是在Android的Handler系列源码中,Looper的对象便是通过ThreadLocal进行存储;ThreadLocal作用就是保证存储对象在一个线程唯一。
二、使用方法
使用Handler中的源码示例:
ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()
sThreadLocal.set(new Looper(quitAllowed));
sThreadLocal.get();
使用上很简单,需要指定泛型,然后set对象就行;使用直接调用get方法获取存储对象。
三、源码分析
看完源码分析,再回头看这幅图可能就能理解。
源码我们先从get方法入手。
/**
* 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.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
仔细查看,首先通过ThreadLocalMap map = getMap(t)这一行代码,获取了一个ThreadLocalMap对象,再通过map对象以自己为kay调用了getEntry(this)方法,获取了value值返回。我们先看下getMap方法。
/**
* 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;
}
方法简单,直接返回的线程的threadLocals属性对象。其实这也是ThreadLocal保证存储对象在一个线程唯一的核心。下面我们再看拿到map之后怎么通过ThreadLocalMap.Entry e = map.getEntry(this)拿到值的。进入getEntry方法。
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
方法内的关键点是通过传入的ThreadLocal的threadLocalHashCode属性作为index在table数组中获取到值;所以我们再去看下ThreadLocal的threadLocalHashCode属性。
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
* inheritableThreadLocals). The ThreadLocal objects act as keys,
* searched via threadLocalHashCode. This is a custom hash code
* (useful only within ThreadLocalMaps) that eliminates collisions
* in the common case where consecutively constructed ThreadLocals
* are used by the same threads, while remaining well-behaved in
* less common cases.
*/
private final int threadLocalHashCode = nextHashCode();
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
可以不用看懂哈,反正就是一个唯一值,作为kay最终去线程的ThreadLocalMap中取值。
如果没有获取到存储对象怎么办?或者说ThreadLocalMap对象就不存在!接着往下看,最终是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 map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
首先第一行调用了initialValue()方法,返回的就是一个null值,可以进行重写;接着往下走,如果ThreadLocalMap存在,就set对象值,如果不存在,进入createMap方法创建ThreadLocalMap并赋值对象值。
/**
* 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}.
*
* <p>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;
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @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方法的逻辑,ThreadLocal通过本身的threadLocalHashCode做为kay去当前Thread中的ThreadLocalMap中获取存储对象。如果获取为null,将调用setInitialValue()方法,进行ThreadLocalMap对象创建或赋值对象值,所以通过重写initialValue方法可以实现设置初始值。
分析完get方法,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);
}
同get方法一样,获取当前线程,再获取ThreadLocalMap对象,如果存在就把对象值给进去,如果不存在同样调用createMap方法进行创建ThreadLocalMap同时赋对象值。
总结
需要注意ThreadLocalMap是在ThreadLocal中。
ThreadLocal保证存储对象在一个线程唯一的核心,就是对象的存取都是通过操作当前线程对象,也就是对象始终存储在当前线程的ThreadLocalMap中,通过ThreadLocal作为kay(threadLocalHashCode)存储唯一对象。