ThreadLocal
- 顾名思义:线程本地的对象。一个ThreadLocal对象,在不同线程中,通过
public T get()
函数返回的value是不同的。 - 在Android的Looper.java中,使用了ThreadLocal对象存储线程自己的Looper对象,通过
myLooper
函数获取looper对象。 - ThreadLocal存储的对象类型是T,
ThreadLocal<T>
. 一个ThreadLocal在每个线程中只能存储一个T类型的value值。ThreadLocal对象是存储在本线程持有的threadLocalMap中。
java/lang/ThreadLocal.java
/**
* 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); // 获取当前Thread的 Map
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 每个线程都含有一个threadLocalMap。因为ThreadLocal对象是存储在map中的,ThreadLocal对象的T类型的value也是和线程对象强关联的。不同线程内的value值是不同。
java/lang/ThreadLocal.java
/**
* 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; // 获取本线程对应的map
}
java/lang/Thread.java
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
map结构
- Thread中的Map是一个类似hashMap的结构。有一个
table[]
数组,数组的类型是Entry类型。Entry实际是由ThreadLocal - T类型 组成的键值对组。 - 每一个ThreadLocal对象都有一个默认的hash值,hash值的计算方式是通过一个AtomicInteger自增的,可以避免hash冲突。
- ThreadLocalMap 是通过ThreadLocal对象的hash值来查找Entry键值对的。
/**
* 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); // hash值是通过AtomicInteger进行自增的。
}
/**
* 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); // 通过hash值获取Entry的数组下标
Entry e = table[i];
if (e != null && e.get() == key)
return e; // T 类型的value值。
else
return getEntryAfterMiss(key, i, e);
}
initialValue 函数初始化ThreadLocal对象
initialValue
函数。通过在子类重写该函数可以初始化ThreadLocal变量。get
当首次get value时,如果没有初始化,那么会通过initialValue
初始化ThreadLocal对象的map值。
/**
* 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(); // 本线程的map中value为空,尝试初始化
}
/**
* 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(); // 可以重写这个函数,给ThreadLocal默认的初值。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
- 示例:EventBus中 通过重写initialValue,保证每个线程首次使用ThreadLocal时,能够获取一个初始化值。
private final ThreadLocal<PostingThreadState> currentPostingThreadState =
new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
内存泄露
- 直接给ThreadLocal对象赋值为null,不能彻底防止value对象的内存泄露(线程没有正确的停止,或者线程池场景)。
- 建议:需要通过
remove
函数来移除map中的Entry,防止内存泄露。 - https://segmentfault.com/a/1190000022704085
- https://zhuanlan.zhihu.com/p/56214714