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
的角色。