ThreadLocal 源码分析

前言

ThreadLocal 用于存储线程级的变量,每个线程各自存各自的,也各自取各自的。Looper 类中就使用它存储了每个线程的 Looper 对象,那么它到底是如何做到的呢,来探究下源码。

基本使用

下面以一个最简单的例子为例

fun main() {
val local = object : ThreadLocal<String>() {
override fun initialValue(): String {
return "unknown"
}
}
println("${Thread.currentThread().name}: ${local.get()}")
local.set(Thread.currentThread().name)
thread {
println("before set ${Thread.currentThread().name}: ${local.get()}")
local.set(Thread.currentThread().name)
println("after set ${Thread.currentThread().name}: ${local.get()}")
}
println("${Thread.currentThread().name}: ${local.get()}")
}

最终输出结果为

main: unknown
main: main
before set Thread-0: unknown
after set Thread-0: Thread-0

显然主线程设置的值,只能主线程取到,子线程取不到的,只有当当前线程本身设置了才能取到。

注:initialValue 方法用于提供默认值,如果需要有默认值那么可以重写。

源码分析

核心就是 set 以及 get 方法,先来看看 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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

就是从当前线程中获取 threadLocals 这么一个 ThreadLocalMap 然后将当前实例当做 key,传入值当做 value 存入进去。接着看看 get 方法。

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

逻辑也是从当前线程中获取 threadLocals 这么一个 ThreadLocalMap 然后根据当前 ThreadLocal 实例获取到存储的 value 值,如果没有值,那么就获取初始化值。

总结

源码分析结束了逻辑就是每个线程都有一个 ThreadLocalMap 实例,可以存储 ThreadLocal 实例以及其对应的 Value。具体的 ThreadLocalMap 内部逻辑就不看了,就当它是个 Map。

比如有一个 ThreadLocal 实例为 @123456,那么主线程的 ThreadLocalMap 中会存储一个 [@123456, Value] 这么一个键值对,子线程的 ThreadLocalMap 也会存储一个 [@123456, Value] 这么一个键值对,取数据时从当前线程取,也就不会相互影响了同时也没有线程同步问题。

0%