这个东东大家可能平时会听说过,但是了解可能不是很多,当然网上也有了很多介绍文章,这里我只按照我的理解来说一下。知道这个东西应该挺多都是看 Looper 看到的。
我们先看看这个东西都是怎么用的:
private final Handler uiThreadHandler = new Handler(Looper.getMainLooper());
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
int mainThreadNum = 1;int workerThreadNum = 2;
public void testThreadLocal() {
threadLocal.set(mainThreadNum);
new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set(workerThreadNum);
uiThreadHandler.post(new Runnable() {
@Override
public void run() {
Log.d("UIThread", "" + threadLocal.get());
}
});
Log.d("WorkerThread", "" + threadLocal.get());
}
}).start();
Log.d("UIThread", "" + threadLocal.get());
}
这里的输出
UIThread: 1
WorkerThread: 2
UIThread: 1
可见,ThreadLocal 的功能就是在不同 Thread set 进去的数据不会相互干扰并且可以直接获取到。
然后我们说一下这个东西是怎么实现的:
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
public ThreadLocal() { }
...
static class ThreadLocalMap {
...
}
}
不管别的,先看构造函数与成员变量。由上可知,可以随意构造实例,并且成员变量只有一个 threadLocalHashCode,所以很明显的可以知道这货就是个皮包公司,它并不存储任何实例,只是包含了一个这个实例的索引,也就是threadLocalHashCode。
那接下来我们在看看它的成员函数,这里只那 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;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这里我们可能就比较清晰为什么 ThreadLocal 可以提供这种可以在不同线程存入/读取数据而互不干扰的功能了。
就是因为实际的变量并不是存入到了 ThreadLocal 中,而是存入到了这个操作对应的 Thread.threadLocals 中了(可以看上边的 getMap 函数)。而且这个 Thread.threadLocals 的实例化也是在 ThreadLocal 的代码中(createMap 函数)。
这个变量在 Thread 中的定义:
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
就是一个引用而已,那现在就指向了 ThreadLocalMap 这个数据结构。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
...
}}
这里边最主要的其实就是这个数组 Entry[] table,这个 Entry 的定义也在上边了,其实就是一个包含了具体变量的 ThreadLocal 的弱引用。
而 threadLocalHashCode 这个变量就是计算具体变量在这个数组中的索引的。
ok,整个的东西已经串下来了。
当然,这里边疑问还有很多,比如 threadLocalHashCode 这个具体的计算方式,这个 table 数组是如何扩容等问题,大家可以自行看代码了。