Java中的ThreadLocal

Android的消息机制中,创建Handler需要一个Looper,如果不在构造器中指定,会自动获取当前线程的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