简介
这边博客主要为《Android开发艺术》的学习笔记。
ThreadLocal并不是线程,而是一个线程内部的数据存储类,可以在每个线程中存储数据。
使用场景
1 当某些数据是以线程为作用域且不同线程具有不同的数据副本,可考虑采用。
2 复杂逻辑下的参数传递(函数调用栈比较深以及代码入口多样性)。
主要方法
ThreadLocal主要使用set和get方法存取数据。
set()源码
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
set方法可传递的参数是一个泛型。在Thread类内部有一个成员专门存储线程的ThreadLocal数据。设置数据时,首先查找当前线程的ThreadLocal数据,如果为空,则先初始化,不为空则存储值。
下面看一下ThreadLocal.Values的put方法:
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
ThreadLocal的值为保存在Values的table数组里,主要存储规则就是找到当前ThreadLocal的reference对象在table数组的索引,将值设置在reference对象的下一个位置。了解这个规则之后就可以分析其get()方法。
get()源码
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
get()方法与set()方法类似,首先取出当前线程的ThreadLocal数据,为空则返回初始值,不为空则查找当前ThreadLocal的reference对象所在索引,然后返回下一个位置存储的数据。