Java 中的 ThreadLocal
立泉在 Android 的消息机制中创建Handler需要一个Looper,如果不在构造器中指定会从当前线程获取:
// Handler 构造器,获取当前线程的 Looper
mLooper = Looper.myLooper();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里是使用ThreadLocal获取创建Handler时所在线程的Looper,再看一个例子:
fun main(args: Array<String>) {
val threadLocal = ThreadLocal<Int>()
Thread {
var i = 0
while (true) {
threadLocal.set(i++)
Thread.sleep(500)
println("Thread-1 get " + threadLocal.get())
}
}.start()
Thread {
var i = 0
while (true) {
Thread.sleep(500)
println("Thread-2 get " + threadLocal.get())
}
}.start()
...
}
启动 2 个线程,Thread-1和Thread-2使用同一个ThreadLocal对象分别在各自线程中读写数据,实际打印日志发现,即使Thread-1一直在向ThreadLocal中写数据,Thread-2读到的永远都是null,而Thread-1却可以正常的读出数据。即一个线程只能通过TheadLocal读取到该线程存储的数据,这就是ThreadLocal的功能。
具体是如何实现的,看一下ThreadLocal存取数据的源码:
// ThreadLocal 用于存储数据的 set() 方法
public void set(T value) {
Thread t = Thread.currentThread();
// 实际存储数据的 map
ThreadLocalMap map = t.threadLocals;
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
// ThreadLocal 用于获取数据的 get() 方法
public T get() {
Thread t = Thread.currentThread();
// 实际存储数据的 map
ThreadLocalMap map = t.threadLocals;
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以看到,数据实际是被保存在Thread内部持有的一个ThreadLocalMap实例对象中:
// Thread 中的 ThreadLocalMap
class Thread {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
存取数据时ThreadLocal只是取出当前线程内部的ThreadLocalMap,再以自己作键向Map存入或取出数据。即数据最终是被保存在线程自己内部持有的对象里,ThreadLocal只是“中间商”,它本身不存储任何数据,只扮演一个Key的角色。