Thread和ThreadLocal
Handler获取当前线程的 Looper 对象,而每一个线程的 Looper 是不一样。每一个线程都有一个 Looper 对象,使用 ThradLocal 去保存和获取当前线程的 Looper 是最佳的选择。
Android中Handler、Looper消息机制得益于巧妙的运用了ThreadLocal的特性,关于Handler机制参考Handler、Looper、MessageQueue机制原理。ThreadLocal很重要,需要单独一章篇幅,仔细讲讲。
1、Thread
在Java中把线程(Thread)当成对象看待,每个线程内部都封装了两个ThreadLocal.ThreadLocalMap类型的成员变量。这里只需要关注threadLocals。
/*
* 与此线程有关的InheritableThreadLocal值。 该Map由InheritableThreadLocal类维护.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/*
* 与此线程有关的ThreadLocal值。 该Map由ThreadLocal类维护。
*/
ThreadLocal.ThreadLocalMap threadLocals = null;另外,Thread类还提供了一个核心static方法:currentThread()。这个就非常方便,可以在任何地方,任何时候获取当前运行的线程。
/**
* 返回对当前正在执行的线程对象的引用。
*
* @return 当前正在执行的线程.
*/
@FastNative
public static native Thread currentThread();也就可以得到当前Thread内部的数据。
2、ThreadLocal
JDK 1.2的版本中就提供java.lang.ThreadLocal
JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>
2.1、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();
}从源码中可以看到,无论调用哪个ThreadLocal实例的get()方法,内部先通过Thread.currentThread()拿到当前运行线程的Thread对象。
getMap(Thread t):
/**
* 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;
}再获取到当前Thread内部的threadLocals对象,也就是前面介绍的Thread内部的成员变量:
ThreadLocal.ThreadLocalMap threadLocals = null;看到这里就会明白,不管在哪里调用访问哪个ThreadLocal对象,最终访问的都是当前运行线程中定义的那个threadLocals。
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;
}如果当前线程的threadLocals为null还没有初始化,就会先创建一个ThreadLocalMap对象赋给当前线程内部的threadLocals。
createMap(Thread t, T firstValue)
/**
* 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);
}2.2、set(T value)
set()方法用于保存当前线程的副本变量值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}注意:
①每个ThreadLocal只能保存一个变量副本,如果想要上线一个线程能够保存多个副本以上,就需要创建多个ThreadLocal。
②ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的风险
③调用ThreadLocal的get()、set()方法时完成后再调用remove方法,将Entry节点和Map的引用关系移除,这样整个Entry对象在GC Roots分析后就变成不可达了,下次GC的时候就可以被回收
相关参考:

