
在多线程编程中,保证数据的一致性和操作的原子性是核心挑战。传统方式如synchronized和Lock通过同步锁来确保线程安全,但锁竞争会带来线程等待,影响性能。ThreadLocal类提供了一种不同的思路:它为每个线程创建变量的独立副本,从根本上避免了共享资源引发的线程安全问题。本文将深入剖析ThreadLocal的源码实现,并探讨其在Android Looper中的经典应用。
ThreadLocal的核心概念
通俗地讲,ThreadLocal可以看作一个以线程(Thread)为键的映射表。它是一个用于创建线程局部变量的类。每个线程访问ThreadLocal变量时,都会获取到只属于当前线程的初始化副本,从而实现了线程间的数据隔离。
深入ThreadLocal源码
ThreadLocal的核心功能围绕set、get和setInitialValue这几个方法展开,它们共同协作管理线程本地变量。
/**
* 用于设置初始值。如果用户覆盖了set()方法,则使用此方法代替set()。
*/
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;
}
/**
* 设置当前线程中此线程局部变量的副本为指定值。
*/
public void set(T value) {
// 1. 获取当前线程
Thread t = Thread.currentThread();
// 2. 获取当前线程关联的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 3. 如果map已存在,则以当前ThreadLocal实例为键存储值
if (map != null)
map.set(this, value);
else
// 4. 否则为当前线程创建新的ThreadLocalMap
createMap(t, value);
}
/**
* 返回当前线程中此线程局部变量的副本值。
*/
public T get() {
// 1. 获取当前线程
Thread t = Thread.currentThread();
// 2. 获取当前线程关联的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 3. 如果map存在且包含对应条目,则返回值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 4. 否则,进行初始化并返回初始值
return setInitialValue();
}
/**
* 获取与ThreadLocal关联的Map。
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* 创建与ThreadLocal关联的Map。
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

(上图图示了Thread、ThreadLocal与ThreadLocalMap三者之间的关系)
ThreadLocalMap 内部类
ThreadLocalMap是ThreadLocal的静态内部类,一个定制化的哈希表,专门用于维护线程本地变量。其条目Entry继承自WeakReference<ThreadLocal<?>>,使用弱引用来持有ThreadLocal键,有助于处理大范围和长生命周期的使用场景,防止内存泄漏。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// ... 其他字段和方法
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在Android Looper中的应用
在Android消息机制中,Looper是每个线程进行消息循环的核心。为了确保每个线程拥有自己独立的Looper实例,系统巧妙地使用了ThreadLocal。
在Looper类中,通过一个静态的final变量来持有ThreadLocal实例,这保证了所有线程访问的是同一个ThreadLocal对象,但通过它获取到的Looper却是线程本地的。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
以下是Looper类中关键方法的实现:
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/** 初始化当前线程为一个Looper线程。 */
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 确保一个线程只能创建一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 将新创建的Looper实例存入当前线程的ThreadLocalMap
sThreadLocal.set(new Looper(quitAllowed));
}
/** 返回与当前线程关联的Looper对象。 */
public static @Nullable Looper myLooper() {
// 从当前线程的ThreadLocalMap中获取Looper
return sThreadLocal.get();
}
}
通过sThreadLocal.set(new Looper(quitAllowed)),每个线程都将自己唯一的Looper对象存储在了自己的ThreadLocalMap中。当需要获取当前线程的Looper时(例如在Handler构造函数中),调用Looper.myLooper(),它内部通过sThreadLocal.get()便能准确无误地取回属于当前线程的那个Looper实例。这是ThreadLocal在Android框架中一个非常经典和高效的应用。