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

arrow_upward